簡體   English   中英

在運行時從Java類路徑中刪除文件夾

[英]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

在測試期間,我們將嘗試加載類FooBar

在類路徑中沒有/ 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

在測試期間,我發現以下情況不起作用。

  • 讓addpath(evilPath);
    tryToLoad( “富”);
    removePath(evilPath); //沒影響
    tryToLoad( “酒吧”);
  • removePath(evilPath);
    tryToLoad( “富”);
    讓addpath(evilPath); //無效
    tryToLoad( “酒吧”);
  • tryToLoad( “富”);
    removePath(evilPath); //無效
    tryToLoad( “酒吧”);

我沒有花時間找出原因。 因為我沒有看到任何實際用途。 如果你真的需要/希望使用類路徑,看看類加載器是如何工作的。

上面的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM