[英]Remove folder from Java classpath at runtime
請在下面找到一個片段作為技術示例來演示添加/刪除路徑。
在任何目錄中創建以下源文件
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
在測試期間,我發現以下情況不起作用。
我沒有花時間找出原因。 因為我沒有看到任何實際用途。 如果你真的需要/希望使用類路徑,看看類加載器是如何工作的。
上面的removePath
方法對我和我的焊接容器都不起作用,url堆棧總是空的。 以下丑陋的沾沾自喜的方法有效:
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:///"));
}
類加載器可以嵌套,而不是修改作為類加載器樹的根的系統類加載器,最好簡單地創建一個嵌套的類加載器並使用它來加載類。
系統類加載器本身是不可變的(有充分理由),但您可以在嵌套類加載器中執行任何操作,包括銷毀它們以卸載類和資源。 這通常用於例如osgi和應用程序服務器來加載/卸載例如插件,應用程序等。
對於嵌套類加載器,您可以完全自定義如何加載類。 URLClassloader
可能是你想要的一個很好的起點。
我不認為有一個直接的方法來做到這一點。 您可以關注:
使用以下命令獲取類路徑變量: System.getenv("CLASSPATH")
。 它將返回半冒號分隔值。
String classPath = System.getenv("CLASSPATH")
將文件夾路徑作為輸入,並將其替換為“”,如:
String remainigPath = classPath.replace(inputpath,"");
使用split方法將其余路徑放在數組中。
String[] paths = remainigPath .split(";");
要添加classPath,您已經擁有了代碼。
我有同樣的問題,所以我通過創建一個庫來處理它,該庫適用於使用URLClassPath
每個ClassLoader
(因此,目前, URLClassLoader
)。
該庫有以下方法:
請注意,由於此庫訪問內部和專有API,因此無法保證在將來的JDK版本中工作。 它目前適用於Java 7和Java 8(Oracle和OpenJDK)。
這是GitHub頁面 (感謝貢獻),這里是Maven Central工件
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.