如何在Java中使用Custom ClassLoader到新的Object

时间:2017-02-17 12:06:23

标签: java classloader

我想创建一个自定义ClassLoader来加载某些路径中的所有jar文件(例如/ home / custom / lib)。

然后我希望每次使用new运算符创建一个Object时,它将在该路径中的所有jar文件中搜索类,然后搜索由参数(-cp)定义的类路径。

有可能吗?

例如,/home/custom/lib/a.jar

中有一个jar文件
中的

public class Main {
    public static void main(String[] args) {
        // do something here to use custom ClassLoader
        // here will search Car in /home/custom/lib/a.jar first then in java class path
        Car car = new Car(); 
    }
}

2 个答案:

答案 0 :(得分:5)

类加载器无法完全按照您的预期执行。

引用相关Q& A的another answer

  

Java将始终使用加载正在执行的代码的类加载器。

所以用你的例子:

public static void main(String[] args) {
    // whatever you do here...
    Car car = new Car(); // ← this code is already bound to system class loader
}

你可以得到的最接近的是使用子优先(父 - 最后)类加载器,例如this one,将它与你的jar一起使用,然后使用反射来创建一个来自那个罐子的Car的实例。

Car内的

a.jar课程:

package com.acme;
public class Car {
    public String honk() {
        return "Honk honk!";
    }
}

您的主要申请:

public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    ParentLastURLClassLoader classLoader = new ParentLastURLClassLoader(
            Arrays.asList(new File("/home/lib/custom/a.jar").toURI().toURL()));
    Class<?> carClass = classLoader.loadClass("com.acme.Car");
    Object someCar = carClass.newInstance();
    Object result = carClass.getMethod("honk").invoke(someCar);
    System.out.println(result); // Honk honk!
}

要注意:如果你的类路径中也有一个com.acme.Car类,那就不是同一个类,因为一个类是由它的全名和类加载器标识的。

为了说明这一点,假设我使用了我的本地Car类,如下所示,我的自定义类加载器加载了carClass

Car someCar = (Car) carClass.newInstance();
// java.lang.ClassCastException: com.acme.Car cannot be cast to com.acme.Car

可能会让人感到困惑,但这只是因为这个名字并不能识别这个类。该演员表无效,因为2个班级不同。它们可能有不同的成员,或者它们可能具有相同的成员但实现不同,或者它们可能是逐字节相同的:它们不是同一个类。

现在,这不是一件非常有用的事情 当jar中的自定义类实现一个通用API时,这些东西变得非常有用,主程序知道如何使用。

例如,假设接口Vehicle(方法String honk())位于公共类路径中,而Car位于a.jar并实现Vehicle }。

ParentLastURLClassLoader classLoader = new ParentLastURLClassLoader(
        Arrays.asList(new File("/home/lib/custom/a.jar").toURI().toURL()));
Class<?> carClass = classLoader.loadClass("com.acme.Car");
Vehicle someCar = (Vehicle) carClass.newInstance(); // Now more useful
String result = someCar.honk(); // can use methods as normal
System.out.println(result); // Honk honk!

这类似于servlet容器的作用:

  • 您的应用程序实现了servlet API(例如,实现javax.servlet.Servlet的类)
  • 将它打包到war文件中,servlet容器可以使用自定义类加载器加载
  • 部署描述符(web.xml文件)告诉servlet容器需要实例化的servlet(类)的名称(如上所述)
  • 这些类是Servlet,servlet容器可以这样使用它们

答案 1 :(得分:0)

在您的情况下,您不需要编写新的ClassLoader,因为您唯一要做的就是在运行时扩展类路径。 为此,您将获得当前的SystemClassLoader实例,并使用 URLClassLoader 将类路径条目添加到其中。

JDK 8的工作示例:

Car class 已编译并位于 C:\ Users \ xxxx \ Documents \ sources \ test \ target \ classes

public class Car {
    public String prout() {
        return "Test test!";
    }
}

主要课程

public static void main(String args[]) throws Exception {
    addPath("C:\\Users\\xxxx\\Documents\\sources\\test\\target\\classes");
    Class clazz = ClassLoader.getSystemClassLoader().loadClass("Car");
    Object car = clazz.newInstance();
    System.out.println(clazz.getMethod("prout").invoke(car));
}

public static void addPath(String s) throws Exception {
    File f=new File(s);
    URL u=f.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});
}
  • 请注意我们需要使用反射,因为方法 addURL(URL u) 受保护
  • 还要注意,因为我们将类路径条目添加到SystemClassloader,所以每次需要时都不需要添加类路径条目,只需一次就足够了,然后使用ClassLoader.getSystemClassLoader().loadClass(String name)从以前添加的类路径加载类条目。

如果您不需要该类路径条目供以后使用,您可以实例化您自己的URLClassLoader实例并相应地加载类,而不是在SystemClassLoader上设置类路径条目。 即:

public static void main(String[] args) {

        try {
            File file = new File("c:\\other_classes\\");
            //convert the file to URL format
            URL url = file.toURI().toURL();
            URL[] urls = new URL[]{ url };
            //load this folder into Class loader
            ClassLoader cl = new URLClassLoader(urls);
            //load the Address class in 'c:\\other_classes\\'
            Class cls = cl.loadClass("com.mkyong.io.Address");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
}

源: https://www.mkyong.com/java/how-to-load-classes-which-are-not-in-your-classpath/

  

问题:我想创建一个自定义ClassLoader来加载所有jar文件   在某些路径中(例如/ home / custom / lib)。

     

然后我希望每次使用new运算符创建一个Object时   它将在该路径中的所有jar文件中搜索类,然后搜索   由参数(-cp)定义的类路径。

     

有可能吗?

如果您希望能够使用new关键字,则需要修改编译器javac -classpath path的类路径 否则在编译时它不知道从哪里加载类。

编译器正在加载类以进行类型检查。 (此处有更多信息:http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html#searching

由于编译器内部实现了new关键字,因此无法在运行时对自定义ClassLoader加载的类使用new关键字。

编译器和JVM(运行时)有自己的ClassLoaders,你不能自定义javac类加载器,据我所知,唯一可以从编译器自定义的部分是注释处理。