繁体   English   中英

如何在Java中使用自定义ClassLoader来新建对象

[英]How to use Custom ClassLoader to new Object in Java

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

那么我希望每次使用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(); 
    }
}

类加载器无法完全完成您期望的操作。

引用相关问答的另一个答案

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

因此,以您的示例为例:

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

你可以得到最接近的将是使用儿童第一(父最后一)类加载器 ,如这一个 ,你的罐子实例化,然后使用反射来创建的实例Car从罐子。

a.jar Car类别:

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加载了carClass

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

可能会造成混淆,但这是因为仅名称不能识别类。 该转换无效,因为这两个类不同。 它们可能具有不同的成员,或者它们可能具有相同的成员,但实现方式不同,或者它们可能是字节对字节相同的:它们不是同一类。

现在,这不是一件非常有用的事情。
当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容器可以这样使用它们

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

JDK 8的工作示例:

车类已 编译并位于 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来加载某个路径(例如/ home / custom / lib)中的所有jar文件。

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

可能吗?

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

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

这是不可能使用new的关键字通过在运行一个定制的ClassLoader加载的类由于编译器内部实现new关键字。

编译器和JVM(运行时)都有自己的ClassLoader,您无法自定义javac classloader,据我所知,唯一可以从编译器中自定义的部分是注释处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM