Java 杂谈Java理论知识集源码分析

Java自定义类加载器

2018-10-11  本文已影响8人  树生1995

自定义类加载器的应用场景

双亲委派

protected synchronized Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
        // 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 thrown if class not found
                // from the non-null parent class loader
                //父类加载器无法完成类加载请求
            }
            if (c == null) {
                // If still not found, then invoke findClass in order to find the class
                //子加载器进行类加载 
                c = findClass(name);
            }
        }
        if (resolve) {
            //判断是否需要链接过程,参数传入
            resolveClass(c);
        }
        return c;
    }

双亲委派模型工作过程如下:

(1) 类加载器从已加载的类中查询该类是否已加载,如果已加载则直接返回。

(2)如果在已加载的类中未找到该类,则委托给父类加载器去加载c = parent.loadClass(name, false),父类也会采用同样的策略查看自己加载的类中是否包含该类,如果没有则委托给父类,以此类推一直到启动类加载起。

(3)如果启动类加载器加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用拓展类加载器来尝试加载,继续失败则会使用AppClassLoader来加载,继续失败则会抛出一个异常ClassNotFoundException,然后再调用当前加载器的findClass()方法进行加载。

双亲委派的好处:

(1)避免自己编写的类动态替换java的核心类,比如String

(2)避免了类的重复加载,因为JVM区分不同类的方式不仅仅根据类名,相同的class文件被不同的类加载器加载产生的是两个不同的类。

正题:自定义类加载器

从上面的源码可以看出调用classLoader时会先根据委派模型在父类加在其中加载,如果加载失败则会加载当前加载器的findClass方法来加载,
因此我们自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法。

public class People {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
 
public class MyClassLoader extends ClassLoader
{
    public MyClassLoader(){}
    
    public MyClassLoader(ClassLoader parent)
    {
        super(parent);
    }
    
    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
        File file = new File("D:/People.class");
        try{
            byte[] bytes = getClassBytes(file);
            //defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class
            Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
            return c;
        } 
        catch (Exception e)
        {
            e.printStackTrace();
        }
        
        return super.findClass(name);
    }
    
    private byte[] getClassBytes(File file) throws Exception
    {
        // 这里要读入.class的字节,因此要使用字节流
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer by = ByteBuffer.allocate(1024);
        
        while (true){
            int i = fc.read(by);
            if (i == 0 || i == -1)
            break;
            by.flip();
            wbc.write(by);
            by.clear();
        }
        fis.close();
        return baos.toByteArray();
    }
}
MyClassLoader mcl = new MyClassLoader(); 
Class<?> clazz = Class.forName("People", true, mcl); 
Object obj = clazz.newInstance();

System.out.println(obj);
//打印出我们的自定义类加载器
System.out.println(obj.getClass().getClassLoader());

参考链接:
https://www.cnblogs.com/gdpuzxs/p/7044963.html
https://blog.csdn.net/seu_calvin/article/details/52315125

上一篇下一篇

猜你喜欢

热点阅读