[英]Remove folder from Java classpath at runtime
Please find below a snippet as technical example to demonstrate adding / removing a path. 请在下面找到一个片段作为技术示例来演示添加/删除路径。
create following source files in any directory 在任何目录中创建以下源文件
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Stack;
import sun.misc.URLClassPath;
public class EvilPathDemo {
public static void addPath(String path) throws Exception {
URL u = new File(path).toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader)
ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL",
new Class[]{URL.class}
);
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u});
}
public static void removePath(String path) throws Exception {
URL url = new File(path).toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader)
ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Field ucpField = urlClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);
Class<?> ucpClass = URLClassPath.class;
Field urlsField = ucpClass.getDeclaredField("urls");
urlsField.setAccessible(true);
Stack urls = (Stack) urlsField.get(ucp);
urls.remove(url);
}
public static void main(String[] args) throws Exception {
String parm = args.length == 1 ? args[0] : "";
String evilPath = "/tmp";
String classpath = System.getProperty("java.class.path");
boolean isEvilPathSet = false;
for (String path : classpath.split(File.pathSeparator)) {
if (path.equalsIgnoreCase(evilPath)) {
System.out.printf("evil path '%s' in classpath%n", evilPath);
isEvilPathSet = true;
break;
}
}
if (isEvilPathSet && parm.equalsIgnoreCase("REMOVE")) {
System.out.printf("evil path '%s' will be removed%n", evilPath);
removePath(evilPath);
}
tryToLoad("Foo");
if (parm.equalsIgnoreCase("ADD")) {
System.out.printf("evil path '%s' will be added%n", evilPath);
addPath(evilPath);
}
tryToLoad("Bar");
}
private static void tryToLoad(String className) {
try {
Class<?> foo = Class.forName(className);
System.out.printf("class loaded: %s%n", foo.getName());
} catch (ClassNotFoundException ex) {
System.out.println(ex);
}
}
}
. 。
public class Foo {
static {
System.out.println("I'm foo...");
}
}
. 。
public class Bar {
static {
System.out.println("I'm bar...");
}
}
compile them as follow 编译如下
javac EvilPathDemo.java
javac -d /tmp Foo.java Bar.java
During the test we will try to load the classes Foo
and Bar
. 在测试期间,我们将尝试加载类
Foo
和Bar
。
without /tmp in the classpath 在类路径中没有/ tmp
java -cp . EvilPathDemo
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar
adding /tmp to the classpath 将/ tmp添加到类路径中
java -cp . EvilPathDemo add
java.lang.ClassNotFoundException: Foo
evil path '/tmp' will be added
I'm bar...
class loaded: Bar
with /tmp in the classpath 在类路径中使用/ tmp
java -cp .:/tmp EvilPathDemo
evil path '/tmp' in the classpath
I'm foo...
class loaded: Foo
I'm bar...
class loaded: Bar
remove /tmp from the classpath 从类路径中删除/ tmp
java -cp .:/tmp EvilPathDemo remove
evil path '/tmp' in the classpath
evil path '/tmp' will be removed
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar
During the testing I found out that following cases are not working. 在测试期间,我发现以下情况不起作用。
I did not spent time to find out why. 我没有花时间找出原因。 Because I don't see any practical use in it.
因为我没有看到任何实际用途。 If you really need/wish to play with the classpaths have a look how classloaders are working.
如果你真的需要/希望使用类路径,看看类加载器是如何工作的。
The removePath
method from above did not work for me and my Weld Container, the url stack was always emtpy. 上面的
removePath
方法对我和我的焊接容器都不起作用,url堆栈总是空的。 The following ugly smugly method worked: 以下丑陋的沾沾自喜的方法有效:
public static void removeLastClasspathEntry() throws Exception {
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Field ucpField = urlClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);
Field loadersField = URLClassPath.class.getDeclaredField("loaders");
loadersField.setAccessible(true);
List jarEntries = (List) loadersField.get(ucp);
jarEntries.remove(jarEntries.size() - 1);
Field pathField = URLClassPath.class.getDeclaredField("path");
pathField.setAccessible(true);
List pathList = (List) pathField.get(ucp);
URL jarUrl = (URL) pathList.get(pathList.size() - 1);
String jarName = jarUrl.toString();
pathList.remove(pathList.size() - 1);
Field lmapField = URLClassPath.class.getDeclaredField("lmap");
lmapField.setAccessible(true);
Map lmapMap = (Map) lmapField.get(ucp);
lmapMap.remove(jarName.replaceFirst("file:/", "file:///"));
}
Class loaders can be nested so instead of modifying the system class loader which is the root of the tree of class loaders, it is better to simply create a nested classloader and use that to load classes. 类加载器可以嵌套,而不是修改作为类加载器树的根的系统类加载器,最好简单地创建一个嵌套的类加载器并使用它来加载类。
The system classloader itself is immutable (for good reasons) but you can do whatever you want in nested class loaders, including destroying them to unload classes and resources. 系统类加载器本身是不可变的(有充分理由),但您可以在嵌套类加载器中执行任何操作,包括销毁它们以卸载类和资源。 This is commonly used in eg osgi and application servers to load/unload eg plugins, applications, etc.
这通常用于例如osgi和应用程序服务器来加载/卸载例如插件,应用程序等。
For nested class loaders you can completely customize how to load classes. 对于嵌套类加载器,您可以完全自定义如何加载类。 The
URLClassloader
is probably a good starting point for what you want. URLClassloader
可能是你想要的一个很好的起点。
I dont think there is a straight forward way to do it. 我不认为有一个直接的方法来做到这一点。 You can follow :
您可以关注:
Get class path variables using : System.getenv("CLASSPATH")
. 使用以下命令获取类路径变量:
System.getenv("CLASSPATH")
。 It will return semi colon separated values. 它将返回半冒号分隔值。
String classPath = System.getenv("CLASSPATH")
Take the folder path as input and replace it with "" like : 将文件夹路径作为输入,并将其替换为“”,如:
String remainigPath = classPath.replace(inputpath,"");
Put the remaining paths in an array using split method. 使用split方法将其余路径放在数组中。
String[] paths = remainigPath .split(";");
For adding classPath, You already have the code. 要添加classPath,您已经拥有了代码。
I had the same issue, so I tackled it by creating a library that works on every ClassLoader
that uses a URLClassPath
(so, currently, URLClassLoader
). 我有同样的问题,所以我通过创建一个库来处理它,该库适用于使用
URLClassPath
每个ClassLoader
(因此,目前, URLClassLoader
)。
The library has methods for: 该库有以下方法:
Please note that, since this library accesses internal and proprietary APIs, it is no guaranteed to work in future versions of the JDK. 请注意,由于此库访问内部和专有API,因此无法保证在将来的JDK版本中工作。 It currently does for Java 7 and Java 8 (Oracle and OpenJDK).
它目前适用于Java 7和Java 8(Oracle和OpenJDK)。
Here is the GitHub page (contribution is appreciated), and here is the Maven Central artifact 这是GitHub页面 (感谢贡献),这里是Maven Central工件
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.