لماذا لا يستطيع System.setProperty() تغيير مسار الفصل في وقت التشغيل؟

StackOverflow https://stackoverflow.com/questions/271506

  •  06-07-2019
  •  | 
  •  

سؤال

أنا أشير إلى سؤال على تغيير مسار الفصل برمجيا.

قرأت واكتشفت أن هناك بعض الوظائف تحت نظام فئة كـ getproperties حيث يمكننا استرداد الخصائص ومن ثم يمكننا أيضًا تعيينها باستخدام setProperties().

لكن الإجابات التي حصلت عليها كانت أنها لن تنجح.لم أجرب هذا بنفسي، ومع ذلك، سأستقبل المكالمة.

فقط للتوضيح، لماذا توجد طرق setProperty() و getProperty() إذا لم تتمكن من تغييرها في وقت التشغيل.أم أن هذا خاص بخاصية classpath فقط؟

سأكون ممتنًا لو تمكن شخص ما من تقديم سيناريو حيث يكون مفيدًا حقًا؟

هل كانت مفيدة؟

المحلول

يمكنك بالتأكيد تعيين أي خصائص النظام التي تريدها في أي وقت.والسؤال هو هل سيكون لها أي تأثير؟في حالة مسار الفصل، الجواب هو لا.تتم تهيئة مُحمل فئة النظام في وقت مبكر جدًا من تسلسل بدء التشغيل.يقوم بنسخ مسار الفصل إلى هياكل البيانات الخاصة به، ولا تتم قراءة خاصية مسار الفصل مرة أخرى.تغييره لا يؤثر على شيء في النظام.

قد يكون السبب في ذلك ذو شقين.السبب الأقل هو الأداء.قد تحتاج إلى إنشاء نوع ما من بنية البيانات للبحث السريع عن الموارد، وقد تكون إعادة تحليل مسار الفصل في كل مرة غير فعالة.السبب الأكثر أهمية هو الأمن.لا تريد أن تقوم فئة مارقة بتغيير مسار الفئة تحتك وتحميل نسخة مخترقة من فئة أخرى.

نصائح أخرى

تعديل Classpath

على الرغم من أنه لا يمكنك تعيين مسار الفصل باستخدام خصائص النظام (لأن JVM يقرأ خصائص النظام مرة واحدة:عند بدء التشغيل)، لا يزال بإمكانك تغيير مسار الفصل عن طريق استدعاء الملف قسرا addURL طريقة محمل الفصل.لاحظ أن الحل أدناه لا يأخذ في الاعتبار الموضوع الحالي.وبالتالي، قد لا يكون دقيقًا في جميع المواقف.

الحل المثالى

تمت إزالة المصدر الأصلي على موقع Sun الإلكتروني للكود التالي:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;                   

import java.io.File;
import java.io.IOException;

import java.net.URL;
import java.net.URLClassLoader;

/**
 * Allows programs to modify the classpath during runtime.              
 */                                                                     
public class ClassPathUpdater {                                         
  /** Used to find the method signature. */                             
  private static final Class[] PARAMETERS = new Class[]{ URL.class };   

  /** Class containing the private addURL method. */
  private static final Class<?> CLASS_LOADER = URLClassLoader.class;

  /**
   * Adds a new path to the classloader. If the given string points to a file,
   * then that file's parent file (i.e., directory) is used as the
   * directory to add to the classpath. If the given string represents a
   * directory, then the directory is directly added to the classpath.
   *
   * @param s The directory to add to the classpath (or a file, which
   * will relegate to its directory).
   */
  public static void add( String s )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    add( new File( s ) );
  }

  /**
   * Adds a new path to the classloader. If the given file object is
   * a file, then its parent file (i.e., directory) is used as the directory
   * to add to the classpath. If the given string represents a directory,
   * then the directory it represents is added.
   *
   * @param f The directory (or enclosing directory if a file) to add to the
   * classpath.
   */
  public static void add( File f )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    f = f.isDirectory() ? f : f.getParentFile();
    add( f.toURI().toURL() );
  }

  /**
   * Adds a new path to the classloader. The class must point to a directory,
   * not a file.
   *
   * @param url The path to include when searching the classpath.
   */
  public static void add( URL url )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    Method method = CLASS_LOADER.getDeclaredMethod( "addURL", PARAMETERS );
    method.setAccessible( true );
    method.invoke( getClassLoader(), new Object[]{ url } );
  }

  private static URLClassLoader getClassLoader() {
    return (URLClassLoader)ClassLoader.getSystemClassLoader();
  }
}

الرابط لم يعد يعمل: http://forums.sun.com/thread.jspa?threadID=300557

مثال الاستخدام

سيتم إضافة المثال التالي /home/user/dev/java/app/build/com/package إلى مسار الفصل في وقت التشغيل:

try {
  ClassPathUpdater.add( "/home/user/dev/java/app/build/com/package/Filename.class" );
}
catch( Exception e ) {
  e.printStackTrace();
}

يمكن استخدام System.setProperty لتعيين معالج الأمان أو البروتوكول في بداية البرنامج.يحب:

/*
Add the URL handler to the handler property. This informs 
IBMJSSE what URL handler to use to handle the safkeyring 
support. In this case IBMJCE.
*/
System.setProperty("java.protocol.handler.pkgs", "com.ibm.crypto.provider");

أو ل باستخدام SSL:

System.setProperty("javax.net.ssl.keyStore", context.getRealPath(KEYSTORE));
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", context.getRealPath(TRUSTSTORE));
System.setProperty("javax.net.debug", "ssl");
HttpClient httpClient = new HttpClient();
GetMethod httpGet = new GetMethod("https://something.com");
httpClient.executeMethod(httpGet);
return new String(httpGet.getResponseBody());

ولكن حذار، لأنه يغير البيئة في وقت التشغيل لـ الجميع التطبيقات التي تعمل في نفس jvm.
على سبيل المثال، إذا كان أحد التطبيقات يحتاج إلى التشغيل باستخدام saxon والآخر باستخدام xalan وكلاهما يستخدم System.setProperty لتعيين TransformerFactory، فسوف تواجه مشكلة

كما قال في مراقبة System.setProperty شرط،
يمكن أن يكون System.setProperty() دعوة شريرة.

  • إنه معادي للخيط بنسبة 100٪
  • أنه يحتوي على متغيرات فائقة العالمية
  • من الصعب للغاية تصحيح الأخطاء عندما تتغير هذه المتغيرات بشكل غامض في وقت التشغيل

فيما يتعلق بخاصية classpath، as قلت في سؤال سابق, ، لا يمكن تغييره بسهولة أثناء التشغيل.

على وجه الخصوص، يتم استخدام خاصية نظام جافا java.class.path لإنشاء رابط مرتبط عند إنشاء مثيل JRE، ثم لا يتم إعادة القراءة.ولذلك، فإن التغييرات التي تجريها على الخاصية لا تؤثر فعليًا على الجهاز الظاهري الموجود.

هناك أيضًا طريقة لتغيير java.library.path في وقت التشغيل، للقيام بذلك، فقط قم بما يلي:

System.setProperty( "java.library.path", newPath);
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null); // that's the key.

عندما يتم تعيين هذا الحقل الثابت الخاص في فئة ClassLoader على قيمة خالية، في المحاولة التالية لتحميل المكتبة الأصلية، ستتم تهيئة ClassLoader مرة أخرى باستخدام القيمة الجديدة في java.library.path.

الفكرة الأساسية ل getProperty() هو أنه يمكن تكوين البرامج/الكود من خارج JVM، وتمرير الخصائص إلى سطر الأوامر باستخدام java -Dfoo=bar بناء الجملة.

نظرًا لأنك قد ترغب في تكوين سلوك معين في مكونات البرامج الأخرى (مثل مكون التسجيل) في المواقف التي لا يمكنك فيها التحكم في سطر الأوامر - فكر في نشرها في حاوية Servlet - setProperty() يأتي كطريقة سهلة لتغيير الإعدادات برمجيًا، على سبيل المثال، قبل إنشاء أداة التسجيل المساعدة.

المشكلة التي يعرضها classpath المشكلة هي أن البرامج عادةً ما تقرأ خصائص النظام هذه مرة واحدة بالضبط، عند تهيئتها لأول مرة.لذا فإن تغيير مسار الفئة بعد بدء تشغيل JVM لا يغير أي شيء بالنسبة لتطبيقك نفسه، نظرًا لأنه تمت تهيئة JVM بالفعل، وتغيير بعض تكوينات التسجيل بعد حصولك بالفعل على مثيل Logger (أو أي شيء آخر)، لن يكون له أي تأثير أيضًا .

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top