16

我指的是关于以编程方式更改类路径的问题

我阅读并发现System类下有一些函数作为 getproperties,我们可以在其中检索属性,然后也可以使用 setProperties() 设置它。

然而我得到的答案是它不起作用。我自己没有尝试过,但是,我正在接听电话。

只是为了澄清一下,如果这些 setProperty() 和 getProperty() 方法不能在运行时改变它,为什么会存在这些方法。或者这仅特定于类路径属性?

如果有人能提出一个他们真正有帮助的场景,我将不胜感激?

4

5 回答 5

13

您当然可以在任何时间点设置您想要的任何系统属性。问题是,会有影响吗?对于类路径,答案是否定的。系统类加载器在启动序列的早期初始化。它将类路径复制到自己的数据结构中,并且不再读取类路径属性。更改它不会影响系统中的任何内容。

造成这种情况的原因可能有两个。次要原因是性能。您可能需要构建某种数据结构以快速查找资源,并且每次重新解析类路径可能效率低下。更重要的原因是安全性。您不希望流氓类更改您下面的类路径并加载另一个类的受损版本。

于 2008-11-07T13:32:16.043 回答
13

修改类路径

即使您不能使用系统属性设置类路径(因为 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();
}
于 2009-07-29T08:04:13.543 回答
10

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,那么您将遇到麻烦

正如Monitored System.setProperty文章中所说,
System.setProperty() 可能是一个邪恶的调用。

  • 它是 100% 线程敌对的
  • 它包含超全局变量
  • 当这些变量在运行时发生神秘变化时,调试起来非常困难

关于类路径属性,正如我在上一个问题中所说,它不能作为运行时轻易更改。

特别是 java System property java.class.path 用于在 JRE 实例化时构建链接链接,然后不再重新读取。因此,您对属性所做的更改实际上不会对现有虚拟机产生任何影响。

于 2008-11-07T08:26:56.970 回答
3

还有一种方法可以在运行时更改 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 中的新值再次初始化。

于 2011-12-22T00:55:46.810 回答
0

其基本思想getProperty()是可以从 JVM 外部配置程序/代码,使用java -Dfoo=bar语法在命令行上传递属性。

由于您可能希望在无法控制命令行的情况下在其他软件组件(例如日志记录组件)中配置某些行为 - 考虑部署在 Servlet 容器中 -setProperty()作为以编程方式更改的便捷方式设置,例如,在实例化您的日志记录实用程序之前。

该问题表现出的classpath问题是,程序通常只会在首次初始化此类系统属性时仅读取一次。因此,在 JVM 启动后更改类路径不会对您的应用程序本身产生任何影响,因为 JVM 已经初始化,并且在您已经获得 Logger 实例(或其他)之后更改一些日志记录配置,通常也不会产生任何影响.

于 2008-11-07T13:25:57.663 回答