如何在Java中更改类路径?

时间:2021-07-12 01:53:04

How do you change the CLASSPATH of a Java process from within the Java process?

如何从Java进程内部更改Java进程的类路径?


Before you ask me "Why would you want to do that?" I'll explain it shortly.

在你问我“你为什么要这么做?”我将解释它。

When you have a Clojure REPL running it is common to need more jars in your CLASSPATH to load a Clojure source file, and I'd like to do it without having to restart Clojure itself (which is not really an option when using it on Slime on Emacs).

当您有一个正在运行的Clojure REPL时,在您的类路径中需要更多的jar来加载Clojure源文件是很常见的,我希望不需要重新启动Clojure本身(在Emacs的Slime上使用Clojure时,这并不是一个选项)。

That's the reason but I don't want this question tagged as some-weird-language some-weird-editor and be disregarded by the majority of Java developers that may have the answer.

这就是原因所在,但我不希望这个问题被标记为某种奇怪的语言、某种奇怪的编辑器,并被大多数可能知道答案的Java开发人员忽视。

6 个解决方案

#1


78  

Update Q4 2017: as commented below by vda8888, in Java 9, the System java.lang.ClassLoader is no longer a java.net.URLClassLoader.

更新2017年Q4:正如vda8888在下面评论的,在Java 9中,系统Java .lang。ClassLoader不再是java.net.URLClassLoader。

See "Java 9 Migration Guide: The Seven Most Common Challenges"

参见“Java 9迁移指南:七个最常见的挑战”

The class loading strategy that I just described is implemented in a new type and in Java 9 the application class loader is of that type.
That means it is not a URLClassLoader anymore, so the occasional (URLClassLoader) getClass().getClassLoader() or (URLClassLoader) ClassLoader.getSystemClassLoader() sequences will no longer execute.

我刚才描述的类装入策略是在一个新类型中实现的,在Java 9中,应用程序类装入器是那种类型的。这意味着它不再是URLClassLoader了,所以偶尔的(URLClassLoader) getClass(). getclassloader()或(URLClassLoader) classloader () classloader . getsystemloader()序列将不再执行。

java.lang.ModuleLayer would be an alternative approach used in order to influence the modulepath (instead of the classpath). See for instance "Java 9 modules - JPMS basics".

. lang。ModuleLayer是另一种用于影响modulepath(而不是类路径)的方法。参见“Java 9模块—JPMS基础”。


For Java 8 or below:

适用于Java 8或以下:

Some general comments:

一些一般性的评论:

you cannot (in a portable way that's guaranteed to work, see below) change the system classpath. Instead, you need to define a new ClassLoader.

您不能(以一种保证可以工作的可移植方式,请参见下面)更改系统类路径。相反,您需要定义一个新的类加载器。

ClassLoaders work in a hierarchical manner... so any class that makes a static reference to class X needs to be loaded in the same ClassLoader as X, or in a child ClassLoader. You can NOT use any custom ClassLoader to make code loaded by the system ClassLoader link properly, if it wouldn't have done so before. So you need to arrange for your main application code to be run in the custom ClassLoader in addition to the extra code that you locate.
(That being said, cracked-all mentions in the comments this example of extending the URLClassLoader)

classloader以分层的方式工作……因此,任何对类X进行静态引用的类都需要加载到与X相同的类加载器中,或者在子类加载器中。如果以前系统类加载器链接不这样做的话,就不能使用任何自定义类加载器来正确地加载系统类加载器链接所加载的代码。因此,除了找到的额外代码之外,还需要安排在自定义类加载器中运行主应用程序代码。(也就是说,cracked-all在评论中提到了这个扩展URLClassLoader的例子)

And you might consider not writing your own ClassLoader, but just use URLClassLoader instead. Create a URLClassLoader with a url that are not in the parent classloaders url's.

您可能会考虑不编写自己的类加载器,而是使用URLClassLoader。创建一个URLClassLoader,其url不在父类加载器的url中。

URL[] url={new URL("file://foo")};
URLClassLoader loader = new URLClassLoader(url);

A more complete solution would be:

更完整的解决办法是:

ClassLoader currentThreadClassLoader
 = Thread.currentThread().getContextClassLoader();

// Add the conf dir to the classpath
// Chain the current thread classloader
URLClassLoader urlClassLoader
 = new URLClassLoader(new URL[]{new File("mtFile").toURL()},
                      currentThreadClassLoader);

// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(urlClassLoader);

If you assume the JVMs system classloader is a URLClassLoader (which may not be true for all JVMs), you can use reflection as well to actually modify the system classpath... (but that's a hack;)):

如果您假设jvm系统类加载器是URLClassLoader(这可能不是对所有jvm都适用),那么您也可以使用反射来实际修改系统类路径……(但这是一个黑客;)):

public void addURL(URL url) throws Exception {
  URLClassLoader classLoader
         = (URLClassLoader) ClassLoader.getSystemClassLoader();
  Class clazz= URLClassLoader.class;

  // Use reflection
  Method method= clazz.getDeclaredMethod("addURL", new Class[] { URL.class });
  method.setAccessible(true);
  method.invoke(classLoader, new Object[] { url });
}

addURL(new File("conf").toURL());

// This should work now!
Thread.currentThread().getContextClassLoader().getResourceAsStream("context.xml");

#2


3  

I don't believe you can - the right thing to do (I believe) is create a new classloader with the new path. Alternatively, you could write your own classloader which allows you to change the classpath (for that loader) dynamically.

我不相信你能——正确的做法(我相信)是用新的路径创建一个新的类加载器。或者,您可以编写自己的类加载器,使您能够动态地更改类路径(对于该加载程序)。

#3


2  

There's no need to write your own class loader! There's clojure.lang.DynamicClassLoader.

没有必要编写自己的类装入器!clojure.lang.DynamicClassLoader。

http://blog.japila.pl/2011/01/dynamically-redefining-classpath-in-clojure-repl/

http://blog.japila.pl/2011/01/dynamically-redefining-classpath-in-clojure-repl/

#4


1  

You may want to look into using java.net.URLClassLoader. It allows you to programmatically load classes that weren't originally in your classpath, though I'm not sure if that's exactly what you need.

您可能希望了解如何使用java.net.URLClassLoader。它允许您以编程方式加载类,这些类最初不在类路径中,但我不确定这是否正是您所需要的。

#5


1  

It is possible as seen from the two links below, the method VonC gives seems to be the best but check out some of these posts and google for "Java Dynamic Classpath" or "Java Dynamic Class Loading" and find out some info from there.

从下面的两个链接中可以看出,VonC提供的方法似乎是最好的,但是可以查看一些post和谷歌的“Java动态类路径”或“Java动态类加载”,并从中找到一些信息。

I'd post in more depth but VonC has pretty much done the job.

我会更深入地发布,但是VonC已经完成了大部分工作。

From Dynamic loading of class and Jar files.

从动态加载类和Jar文件。

Also check this sun forum post.

也检查这个阳光论坛帖子。

#6


-3  

String s="java  -classpath abcd/ "+pgmname+" "+filename;   
Process pro2 = Runtime.getRuntime().exec(s); 
BufferedReader in = new BufferedReader(new InputStreamReader(pro2.getInputStream()));

is an example of changin the classpath in java program

java程序中的类路径是否发生了变化

#1


78  

Update Q4 2017: as commented below by vda8888, in Java 9, the System java.lang.ClassLoader is no longer a java.net.URLClassLoader.

更新2017年Q4:正如vda8888在下面评论的,在Java 9中,系统Java .lang。ClassLoader不再是java.net.URLClassLoader。

See "Java 9 Migration Guide: The Seven Most Common Challenges"

参见“Java 9迁移指南:七个最常见的挑战”

The class loading strategy that I just described is implemented in a new type and in Java 9 the application class loader is of that type.
That means it is not a URLClassLoader anymore, so the occasional (URLClassLoader) getClass().getClassLoader() or (URLClassLoader) ClassLoader.getSystemClassLoader() sequences will no longer execute.

我刚才描述的类装入策略是在一个新类型中实现的,在Java 9中,应用程序类装入器是那种类型的。这意味着它不再是URLClassLoader了,所以偶尔的(URLClassLoader) getClass(). getclassloader()或(URLClassLoader) classloader () classloader . getsystemloader()序列将不再执行。

java.lang.ModuleLayer would be an alternative approach used in order to influence the modulepath (instead of the classpath). See for instance "Java 9 modules - JPMS basics".

. lang。ModuleLayer是另一种用于影响modulepath(而不是类路径)的方法。参见“Java 9模块—JPMS基础”。


For Java 8 or below:

适用于Java 8或以下:

Some general comments:

一些一般性的评论:

you cannot (in a portable way that's guaranteed to work, see below) change the system classpath. Instead, you need to define a new ClassLoader.

您不能(以一种保证可以工作的可移植方式,请参见下面)更改系统类路径。相反,您需要定义一个新的类加载器。

ClassLoaders work in a hierarchical manner... so any class that makes a static reference to class X needs to be loaded in the same ClassLoader as X, or in a child ClassLoader. You can NOT use any custom ClassLoader to make code loaded by the system ClassLoader link properly, if it wouldn't have done so before. So you need to arrange for your main application code to be run in the custom ClassLoader in addition to the extra code that you locate.
(That being said, cracked-all mentions in the comments this example of extending the URLClassLoader)

classloader以分层的方式工作……因此,任何对类X进行静态引用的类都需要加载到与X相同的类加载器中,或者在子类加载器中。如果以前系统类加载器链接不这样做的话,就不能使用任何自定义类加载器来正确地加载系统类加载器链接所加载的代码。因此,除了找到的额外代码之外,还需要安排在自定义类加载器中运行主应用程序代码。(也就是说,cracked-all在评论中提到了这个扩展URLClassLoader的例子)

And you might consider not writing your own ClassLoader, but just use URLClassLoader instead. Create a URLClassLoader with a url that are not in the parent classloaders url's.

您可能会考虑不编写自己的类加载器,而是使用URLClassLoader。创建一个URLClassLoader,其url不在父类加载器的url中。

URL[] url={new URL("file://foo")};
URLClassLoader loader = new URLClassLoader(url);

A more complete solution would be:

更完整的解决办法是:

ClassLoader currentThreadClassLoader
 = Thread.currentThread().getContextClassLoader();

// Add the conf dir to the classpath
// Chain the current thread classloader
URLClassLoader urlClassLoader
 = new URLClassLoader(new URL[]{new File("mtFile").toURL()},
                      currentThreadClassLoader);

// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(urlClassLoader);

If you assume the JVMs system classloader is a URLClassLoader (which may not be true for all JVMs), you can use reflection as well to actually modify the system classpath... (but that's a hack;)):

如果您假设jvm系统类加载器是URLClassLoader(这可能不是对所有jvm都适用),那么您也可以使用反射来实际修改系统类路径……(但这是一个黑客;)):

public void addURL(URL url) throws Exception {
  URLClassLoader classLoader
         = (URLClassLoader) ClassLoader.getSystemClassLoader();
  Class clazz= URLClassLoader.class;

  // Use reflection
  Method method= clazz.getDeclaredMethod("addURL", new Class[] { URL.class });
  method.setAccessible(true);
  method.invoke(classLoader, new Object[] { url });
}

addURL(new File("conf").toURL());

// This should work now!
Thread.currentThread().getContextClassLoader().getResourceAsStream("context.xml");

#2


3  

I don't believe you can - the right thing to do (I believe) is create a new classloader with the new path. Alternatively, you could write your own classloader which allows you to change the classpath (for that loader) dynamically.

我不相信你能——正确的做法(我相信)是用新的路径创建一个新的类加载器。或者,您可以编写自己的类加载器,使您能够动态地更改类路径(对于该加载程序)。

#3


2  

There's no need to write your own class loader! There's clojure.lang.DynamicClassLoader.

没有必要编写自己的类装入器!clojure.lang.DynamicClassLoader。

http://blog.japila.pl/2011/01/dynamically-redefining-classpath-in-clojure-repl/

http://blog.japila.pl/2011/01/dynamically-redefining-classpath-in-clojure-repl/

#4


1  

You may want to look into using java.net.URLClassLoader. It allows you to programmatically load classes that weren't originally in your classpath, though I'm not sure if that's exactly what you need.

您可能希望了解如何使用java.net.URLClassLoader。它允许您以编程方式加载类,这些类最初不在类路径中,但我不确定这是否正是您所需要的。

#5


1  

It is possible as seen from the two links below, the method VonC gives seems to be the best but check out some of these posts and google for "Java Dynamic Classpath" or "Java Dynamic Class Loading" and find out some info from there.

从下面的两个链接中可以看出,VonC提供的方法似乎是最好的,但是可以查看一些post和谷歌的“Java动态类路径”或“Java动态类加载”,并从中找到一些信息。

I'd post in more depth but VonC has pretty much done the job.

我会更深入地发布,但是VonC已经完成了大部分工作。

From Dynamic loading of class and Jar files.

从动态加载类和Jar文件。

Also check this sun forum post.

也检查这个阳光论坛帖子。

#6


-3  

String s="java  -classpath abcd/ "+pgmname+" "+filename;   
Process pro2 = Runtime.getRuntime().exec(s); 
BufferedReader in = new BufferedReader(new InputStreamReader(pro2.getInputStream()));

is an example of changin the classpath in java program

java程序中的类路径是否发生了变化