Java 为什么 System.setProperty() 不能在运行时更改类路径?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/271506/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't System.setProperty() change the classpath at runtime?
提问by Nrj
I am refering to the questionon changing the classpath programmatically.
我指的是以编程方式更改类路径的问题。
I read and found out that there is some function under Systemclass as getproperties where we can retrieve the properties and then also can set it using setProperties().
我阅读并发现System类下有一些函数作为 getproperties,我们可以在其中检索属性,然后还可以使用 setProperties() 对其进行设置。
The answers however I got was that It Wont work. I have not tried this myself, however, i am taking the call.
然而我得到的答案是它行不通。我自己没有尝试过,但是,我正在接听电话。
Just to clarify, then why these setProperty() and getProperty() methods are there if they cannot alter it at run time. Or is this specific to the classpath property only ?
只是为了澄清一下,如果这些 setProperty() 和 getProperty() 方法不能在运行时改变它,那么为什么它们会存在。或者这仅特定于类路径属性?
I will appreciate if someone can present a scenario where they are really helpful?
如果有人能提出一个他们真正有帮助的场景,我将不胜感激?
采纳答案by Martin Probst
You can certainly set any system properties you want at any point of time. The question is, will it have any effect? In the case of classpath, the answer is NO. The system class loader is initialized at a very early point in the startup sequence. It copies the classpath into its own data structures, and the classpath property is not read again. Changing it affect nothing in the system.
您当然可以随时设置所需的任何系统属性。问题是,会不会有影响?对于类路径,答案是否定的。系统类加载器在启动序列的很早的时候被初始化。它将类路径复制到自己的数据结构中,并且不会再次读取类路径属性。更改它对系统没有任何影响。
The reason for this may be two-fold. The lesser reason is performance. You may need to have some sort of data structure built for quick lookup of resources, and re-parsing classpath every time may be inefficient. The more important reason is security. You don't want a rogue class change the classpath under you and load compromised version of another class.
造成这种情况的原因可能有两方面。次要原因是性能。您可能需要为快速查找资源而构建某种数据结构,并且每次重新解析类路径可能效率低下。更重要的原因是安全。您不希望流氓类更改您下的类路径并加载另一个类的受损版本。
回答by VonC
System.setProperty can be used to set some security or protocol handler at the beginning of a program. Like:
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");
or for using SSL:
或使用 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());
But beware, because it changes the environment at runtime for ALLapplications running in the same jvm.
If for example one application needs to run with saxon and the other with xalan and both make use of System.setProperty to set the transformerFactory, then you will run into trouble
但请注意,因为它会在运行时更改运行在同一 jvm 中的所有应用程序的环境。
例如,如果一个应用程序需要使用 saxon 运行而另一个应用程序需要使用 xalan 并且都使用 System.setProperty 来设置transformerFactory,那么您将遇到麻烦
As said in Monitored System.setPropertyarticle,
System.setProperty() can be an evil call.
正如Monitored System.setProperty文章中所说,
System.setProperty() 可能是一个邪恶的调用。
- It is 100% thread-hostile
- It contains super-global variables
- It is extremely difficult to debug when these variables mysteriously change at runtime
- 它是 100% 线程敌对的
- 它包含超全局变量
- 当这些变量在运行时神秘地改变时,调试是极其困难的
Regarding the classpath property, as I said in a previous question, it can not be easily changed as runtime.
关于 classpath 属性,正如我在上一个问题中所说,它不能作为运行时轻松更改。
In particular, java System property java.class.path is used to build a linked link when the JRE is instantiated, then is not re-read. Therefore, changes you make to the property don't really do anything to the existing virtual machine.
特别是 java 系统属性 java.class.path 用于在实例化 JRE 时构建链接链接,然后不重新读取。因此,您对属性所做的更改实际上不会对现有虚拟机产生任何影响。
回答by Martin Probst
The basic idea of getProperty()
is that programs/code can be configured from outside of the JVM, passing properties on the command line using the java -Dfoo=bar
syntax.
的基本思想getProperty()
是可以从 JVM 外部配置程序/代码,使用java -Dfoo=bar
语法在命令行上传递属性。
As you may want to configure certain behaviour in other software components (such as a logging component) in situations where you don't have control over the command line - think being deployed in a Servlet container - setProperty()
comes in as a handy way to programmatically alter settings, e.g., before instantiating your logging utility.
由于您可能希望在无法控制命令行的情况下在其他软件组件(例如日志记录组件)中配置某些行为 - 考虑部署在 Servlet 容器中 -setProperty()
作为一种方便的方式以编程方式更改设置,例如,在实例化您的日志实用程序之前。
The problem that is exhibited by the classpath
issue is that programs will typically only read such system properties exactly once, when they are first initialized. So changing the classpath after JVM startup doesn't change anything for you app itself, because the JVM is already initialized, and changing some logging configuration after you have already obtained a Logger instance (or whatever), typically won't have any effect either.
该问题所表现出的classpath
问题是,程序通常只会在首次初始化此类系统属性时只读取一次。因此,在 JVM 启动后更改类路径不会对您的应用程序本身产生任何影响,因为 JVM 已经初始化,并且在您已经获得 Logger 实例(或其他)之后更改一些日志记录配置,通常也不会产生任何影响.
回答by Dave Jarvis
Modify Classpath
修改类路径
Even though you cannot set the classpath using the system properties (because the JVM reads system properties once: at startup), you can still change the classpath by forcibly invoking the addURL
method of the classloader. Note that the solution below does not take into consideration the current thread. Consequently, it might not be accurate in all situations.
即使您不能使用系统属性设置类路径(因为 JVM 读取系统属性一次:在启动时),您仍然可以通过强制调用addURL
类加载器的方法来更改类路径。请注意,下面的解决方案没有考虑当前线程。因此,它可能并非在所有情况下都准确。
Example Solution
示例解决方案
The original source on Sun's website for the following code has been removed:
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();
}
}
The link no longer works: http://forums.sun.com/thread.jspa?threadID=300557
该链接不再有效:http: //forums.sun.com/thread.jspa?threadID=300557
Example Usage
示例用法
The following example will add /home/user/dev/java/app/build/com/package
to the classpath at runtime:
以下示例将/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();
}
回答by Zorkus
There is also a way to change java.library.path in runtime, to do that, just do:
还有一种方法可以在运行时更改 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.
When this private static field in ClassLoader class is set to null, on next attempt to load native library ClassLoader will be initialized again using the new value in java.library.path.
当 ClassLoader 类中的这个私有静态字段设置为 null 时,下次尝试加载本机库时 ClassLoader 将使用 java.library.path 中的新值再次初始化。