有没有办法从类路径中删除文件夹,类似于在运行时添加文件夹(Can a directory be added to the class path at runtime?)
答案 0 :(得分:5)
请在下面找到一个片段作为技术示例来演示添加/删除路径。
在任何目录中创建以下源文件
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...");
}
}
按照以下方式编译它们
javac EvilPathDemo.java
javac -d /tmp Foo.java Bar.java
在测试期间,我们会尝试加载课程Foo
和Bar
。
在课程路径中没有/ tmp
java -cp . EvilPathDemo
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar
将/ tmp添加到类路径
java -cp . EvilPathDemo add
java.lang.ClassNotFoundException: Foo
evil path '/tmp' will be added
I'm bar...
class loaded: Bar
在类路径中使用/ tmp
java -cp .:/tmp EvilPathDemo
evil path '/tmp' in the classpath
I'm foo...
class loaded: Foo
I'm bar...
class loaded: Bar
从类路径中删除/ 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
在测试期间,我发现以下情况无效。
我没有花时间找出原因。因为我没有看到任何实际用途。如果您确实需要/希望使用类路径,请查看类加载器的工作方式。
答案 1 :(得分:4)
上面的removePath
方法对我和我的焊接容器都不起作用,网址堆栈总是空闲的。
以下丑陋的沾沾自喜的方法有效:
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:///"));
}
答案 2 :(得分:1)
类加载器可以嵌套,而不是修改作为类加载器树的根的系统类加载器,最好简单地创建一个嵌套的类加载器并使用它来加载类。
系统类加载器本身是不可变的(有充分理由),但您可以在嵌套类加载器中执行任何操作,包括销毁它们以卸载类和资源。这通常用于例如用于加载/卸载的osgi和应用程序服务器,例如插件,应用程序等。
对于嵌套类加载器,您可以完全自定义如何加载类。 URLClassloader
可能是你想要的一个很好的起点。
答案 3 :(得分:0)
我认为没有一种直接的方法可以做到这一点。你可以关注:
使用以下代码获取类路径变量:System.getenv("CLASSPATH")
。它将返回半冒号分隔值。
String classPath = System.getenv("CLASSPATH")
将文件夹路径作为输入,并将其替换为“”,如:
String remainigPath = classPath.replace(inputpath,"");
使用拆分方法将其余路径放在数组中。
String[] paths = remainigPath .split(";");
要添加classPath,您已经拥有了代码。
答案 4 :(得分:0)
我遇到了同样的问题,因此我通过创建一个适用于使用ClassLoader
的{{1}} {目前的URLClassPath
)的库来解决这个问题。
该库具有以下方法:
请注意,由于此库访问内部和专有API,因此无法保证在将来的JDK版本中工作。它目前适用于Java 7和Java 8(Oracle和OpenJDK)。
以下是the GitHub page(感谢您的贡献),此处为the Maven Central artifact