Почему System.setProperty () не может изменить путь к классу во время выполнения? - PullRequest
16 голосов
/ 07 ноября 2008

Я ссылаюсь на вопрос о программном изменении пути к классам.

Я прочитал и обнаружил, что в классе System есть некоторая функция как getproperties, где мы можем получить свойства, а затем также установить их с помощью setProperties ().

Ответы, которые я получил, были: «Это не сработает». Сам я этого не пробовал, однако отвечаю на звонок.

Просто чтобы уточнить, почему существуют эти методы setProperty () и getProperty (), если они не могут изменить его во время выполнения. Или это относится только к свойству classpath?

Буду признателен, если кто-нибудь представит сценарий, в котором он действительно полезен?

Ответы [ 5 ]

13 голосов
/ 29 июля 2009

Изменить 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();
}
13 голосов
/ 07 ноября 2008

Вы, безусловно, можете установить любые системные свойства, которые вы хотите в любой момент времени. Вопрос в том, будет ли это иметь какое-либо влияние? В случае classpath ответ НЕТ. Загрузчик системного класса инициализируется на очень ранней стадии последовательности запуска. Он копирует classpath в свои собственные структуры данных, и свойство classpath больше не читается. Изменение его никак не повлияет на систему.

Причина этого может быть двоякой. Меньшая причина - производительность. Возможно, вам понадобится создать некую структуру данных для быстрого поиска ресурсов, и повторный анализ пути к классам каждый раз может оказаться неэффективным. Более важной причиной является безопасность. Вы не хотите, чтобы мошеннический класс изменил путь к вашему классу и загрузил скомпрометированную версию другого класса.

10 голосов
/ 07 ноября 2008

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());

Но будьте осторожны, потому что изменяет среду во время выполнения для ALL приложений, работающих в том же jvm .
Например, если одно приложение должно работать с saxon, а другое с xalan, и оба используют System.setProperty для установки transformerFactory, то вы столкнетесь с проблемой

Как сказано в Отслеживаемая статья System.setProperty ,
System.setProperty () может быть злым вызовом.

  • На 100% враждебно настроен
  • Содержит суперглобальные переменные
  • Крайне сложно отлаживать, когда эти переменные загадочно меняются во время выполнения

Что касается свойства classpath, как я сказал в предыдущем вопросе 1030 *, его нельзя легко изменить как время выполнения.

В частности, свойство java System java.class.path используется для создания связанной ссылки при создании экземпляра JRE, тогда не перечитывается . Поэтому изменения, которые вы вносите в свойство, на самом деле ничего не делают с существующей виртуальной машиной.

3 голосов
/ 22 декабря 2011

Существует также способ изменить 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 задано значение NULL, при следующей попытке загрузки собственной библиотеки ClassLoader будет снова инициализирован с использованием нового значения в java.library.path.

0 голосов
/ 07 ноября 2008

Основная идея getProperty() заключается в том, что программы / код можно настраивать извне JVM, передавая свойства в командной строке с использованием синтаксиса java -Dfoo=bar.

Поскольку вам может потребоваться настроить определенное поведение в других программных компонентах (например, компоненте ведения журнала) в ситуациях, когда вы не контролируете командную строку - подумайте, что развертывание в контейнере сервлетов - setProperty() выглядит как удобный способ программно изменить настройки, например, перед созданием утилиты ведения журнала.

Проблема, которая проявляется в проблеме classpath, заключается в том, что программы обычно читают такие системные свойства только один раз, когда они впервые инициализируются. Таким образом, изменение пути к классу после запуска JVM ничего не меняет для самого приложения, поскольку JVM уже инициализирована, и изменение конфигурации журналирования после того, как вы уже получили экземпляр Logger (или любой другой), как правило, также не будет иметь никакого эффекта. .

...