java的ClassLoader简介

ClassLoader的作用

个人理解,ClassLoader用来从外部空间(硬盘、网络等)向jvm内存空间加载字节码文件,并将其解析为Class对象。

ClassLoader分类

ClassLoader大体可分为4类:BootstrapClassLoader、ExtClassLoader、AppClassLoader以及用户自定义的ClassLoader。

BootstrapClassLoader是JVM启动之后第一个被启动的类加载器,其默认加载 JAVE_HOME/jre/lib目录下的核心库;
同时,BootstrapClassLoader还会生成ExtClassLoader的实例,设置ExtClassLoader的parent为null。ExtClassLoader默认加载JAVE_HOME/jre/lib/ext目录下的扩展包;BootstrapClassLoader还会继续生成AppClassLoader实例,将其parent设置为ExtClassLoader。AppClassLoader默认加载CLASSPATH目录下的包。

由此可见,虽然AppClassLoader的parent是ExtClassLoader,但是AppClassLoader本身是由BootstrapClassLoader创建;虽然BootstrapClassLoader是ExtClassLoader的parent,但是在ExtClassLoader中parent被设置为null。这是因为BootstrapClassLoader是由C语言实现的,并不是一个java类。

双亲委派模式

 ExtClassLoader --> BootstrapClassLoader

 AppClassLoader --> ExtClassLoader

 CustomClassLoader1 --> AppClassLoader

 CustomClassLoader2 --> AppClassLoader

双亲委派模式:当加载一个class的时候,某个ClassLoader首先询问其父ClassLoader(如果存在的话)是否已经加载了该class:如果父ClassLoader已经加载了该class的话,就返回该class的引用;如果父ClassLoader也没有加载此class的话,则本ClassLoader则尝试加载,加载成功就返回引用,加载不成功就抛出ClassNotFoundException。

// 检查类是否已被装载过  
Class c = findLoadedClass(name);  
if (c == null ) {  
     // 指定类未被装载过  
     try {  
         if (parent != null ) {  
             // 如果父类加载器不为空, 则委派给父类加载  
             c = parent.loadClass(name, false );  
         } else {  
             // 如果父类加载器为空, 则委派给启动类加载加载  
             c = findBootstrapClass0(name);  
         }  
     } catch (ClassNotFoundException e) {  
         // 启动类加载器或父类加载器抛出异常后, 当前类加载器将其  
         // 捕获, 并通过findClass方法, 由自身加载  
         c = findClass(name);  
     }  
}  

使用双亲委派模式,在一个项目内可以防止某个class被重复加载,同时可以防止我们自定义的类取代java的核心类。当然,在tomcat中,两个不同的web项目可以在maven中引用相同的class(不同的web项目之间相互不影响,class会被加载两次,这是因为tomcat的类加载机制为子优先模式【双亲委派模式为父优先】)。

自定义类加载器

java提供的类加载器只能加载特定目录下的jar和class,如果我们需要从其他位置加载类的时候,就需要自定义类加载器。
在实现自己的类加载器的时候,只需要继承java.lang.ClassLoader类并重写findClass方法(ClassLoader中loadClass方法在找不到类的时候就会调用findClass方法去查找该类,因此一般只需要重写findClass方法)。以下是通过自定义类加载器加载位于桌面上的Test类,并调用其hello()方法,该方法输出“hello world”字符串


package my.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

public class CustomClassLoader extends ClassLoader {

    private String baseUrl;

    public CustomClassLoader(ClassLoader parent, String baseUrl) {
        super(parent);
        this.baseUrl = baseUrl;
    }

    public CustomClassLoader(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz =  null;
        byte[] data = getClassByteData(name);
        if (data == null){
            throw new ClassNotFoundException();
        }
        clazz = defineClass(name,data,0,data.length);
        return clazz;
    }


    private byte[] getClassByteData(String name){
        InputStream inputStream = null;
        try {
            String path = baseUrl+name.replaceAll("\\.", "/")+".class";
            URL url = new URL(path);
            byte[] buff = new byte[1024*4];
            int len = -1;
            inputStream = url.openStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while ((len=inputStream.read(buff))!=-1){
                byteArrayOutputStream.write(buff,0,len);
            }
            return byteArrayOutputStream.toByteArray();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

}
package my.classloader;

import java.lang.reflect.Method;

public class CustomClassLoaderTest {
    public static void main(String[] args) {
        String baseUrl = "file:///C:/Users/admin/Desktop/";
        CustomClassLoader customClassLoader = new CustomClassLoader(baseUrl);
        String className = "my.classloader.Test";
        try {
            Class clazz = customClassLoader.loadClass(className);
            System.out.println(clazz.getClassLoader());
            Object object = clazz.newInstance();
            Method  method = clazz.getMethod("hello");
            method.invoke(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值