Java如何加载一个外部类

2019-04-07  本文已影响0人  我家造地球

(1)使用javapoet相关api 生成一个类

public class CreateJavaFile {
    public static void main(String[] args) throws IOException {
        
        //生成一个方法体(main)  打印输出一个hello
        MethodSpec methodSpec = MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC,Modifier.STATIC)
                .returns(void.class)
                .addParameter(String[].class,"args")
                .addStatement("System.out.println($S)","hello")
                .build();
        
        //类相关操作
        TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(methodSpec)//添加一个方法体
                .build();
        
        //包名操作
        JavaFile javaFile = JavaFile.builder("com.demo.helloworld",typeSpec).build();
        
        //输出代码到控制台
        javaFile.writeTo(System.out);
        
        //把代码生成到项目根目录 文件名为hello
        javaFile.writeTo(new File("hello"));
    }
}

执行完后,得到如下图。

hello.png

(2)新建一个classLoad类继承URLClassLoader


public class MyClassLoad extends URLClassLoader {
    public MyClassLoad(URL[] urls) {
        super(urls);
    }

    @Override
    public Class<?> loadClass(java.lang.String name) throws ClassNotFoundException {
        System.out.println("loadClass--->"+name);
        return super.loadClass(name);
    }

    @Override
    protected Class<?> findClass(java.lang.String name) throws ClassNotFoundException {
        System.out.println("findClass--->"+name);
        return super.findClass(name);
    }
}

(3)测试,加载我们生成类,并调用方法

public class TestLoadLocalClass {
    public static void main(String[] args) throws Exception {

        File file = new File("hello");

        URL url = file.toURL();
        
        //file.toURL() 方法过时可使用 如下代替
        //URL url = new URL("file:"+file.getAbsolutePath().replace("\\","/")+"/");

        MyClassLoad myClassLoad = new MyClassLoad(new URL[]{url});
        
        //加载类文件,包名+类名  
        //只能加载class文件  我们生成的.java文件 需要用 javac 手动转换一下
        Class aClass = myClassLoad.loadClass("com.demo.helloworld.HelloWorld");

        System.out.println(aClass.getClassLoader());
        
        //使用反射得到一个对象
        Object aObject = aClass.newInstance();
        
        //获取到main方法,方法的参数类型、个数一定要相匹配
        Method method = aClass.getMethod("main", String[].class);

        //执行main方法        
        method.invoke(aObject, new Object[]{new String[]{}});
    }
}

查看classLoad的打印日志,加载类到时候,是先执行loadClass 后执findClass

loadClass--->com.demo.helloworld.HelloWorld
findClass--->com.demo.helloworld.HelloWorld
loadClass--->java.lang.Object
MyClassLoad@12a3a380 //我们自己建的类加载器
loadClass--->java.lang.String
loadClass--->java.lang.System
loadClass--->java.io.PrintStream
hello  //输出类hello

Java语言系统自带有三个类加载器:

  • BootstrapClassLoader 最顶层的加载类,主要加载核心类库
  • ExtentionClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
  • AppclassLoader也称为SystemAppClass 加载当前应用的classpath的所有类。

(4)进入ClassLoader.java 从源码看java类加载机制

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // 首先,检查类是否已经加载
            Class<?> c = findLoadedClass(name);
            
            //如果类没有被加载过,如果加载过直接返回类,否则先使用父加载加载
            if (c == null) {
                try {
                    //如果父加载器不为空则先使用父加载器
                    //否则使用引导类加载器
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    //抛出ClassNotFoundException异常
                }
                
                //如果父加载器没有找到,则使用调用自身的findClass
                //我们使用的是URLClassLoader,有兴趣的话,可以去查看URLClassLoader的findClass()方法
                if (c == null) {
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            //返回我们要使用到类
            return c;
        }
    }

(5)结论

类加载先从父加载器加载,父类没有加载到,则使用自身的加载器加载。

上一篇下一篇

猜你喜欢

热点阅读