Java基础:类加载器

概述

  • 什么是类加载器
    类加载器用来加载类的类:ClassLoader。把.class文件加载到JVM的方法区中,变成一个Class对象
  • 得到类加载器
    Class的方法:getClassLoader()
  • 类加载器的分类
    (1)引导:负责加载类库
    (2)扩展:负责加载扩展jar包
    (3)系统:负责加载应用下的class,包含开发人员写的类,和第三方的jar包。(即classpath下的类)
    分级:引导是扩展的上级,扩展是系统的上级

JVM眼中的类

在jvm中一个类不能被加载两次。如果一个类被加载了,再次加载这个类时,加载器会先去查找这个类是否已经被加载,如果加载就不再加载。
如果一个类使用不同的加载器加载,就可以出现多次加载的情况。在jvm眼中,相同的类需要有相同的.class文件和相同的类加载器。

类的委托机制

在代码中出现 new A();
系统发现自己加载的类中包含:new A();,说明需要去加载A类

系统 扩展 引导 加载A类 加载A类 如果在类库中查到A类,加载并返回对应的Class对象,否则,返回null 如果查到A类,加载并返回对应的Class对象,否则返回null 如果系统查询到A类,返 回加载之,否则抛出异常 ClassNotFou ndException 系统 扩展 引导

通过上面的图解,可以得到,优先级是 引导>扩展>系统。这样的好处就是保证了JDK中的类一定由引导类加载器加载。

注意:如果某一级类加载器加载了A类,那么,该类中出现的其他需要被加载的类,也有该级类加载器加载。

自定义类加载器

我们也可以通过继承ClassLoader类来完成自定义类加载器,自类加载器的目的一般是为了加载网络上的类,因为这会让class在网络中传输,为了安全,那么class一定是需要加密的,所以需要自定义的类加载器来加载(自定义的类加载器需要做解密工作)。
ClassLoader加载类都是通过loadClass()方法来完成的,loadClass()方法的工作流程如下:

  1. 调用findLoadedClass()方法查看该类是否已经被加载过了,如果该没有加载过,那么这个方法返回null;
  2. 判断findLoadedClass()方法返回的是否为null,如果不是null那么直接返回,这可以避免同一个类被加载两次;
  3. 如果findLoadedClass()返回的是null,那么就启动代理模式(委托机制),即调用上级的loadClass()方法,获取上级的方法是getParent(),当然上级可能还有上级,这个动作就一直向上走;
  4. 如果getParent().loadClass()返回的不是null,这说明上级加载成功了,那么就加载结果;
  5. 如果上级返回的是null,这说明需要自己出手了,这时loadClass()方法会调用本类的findClass()方法来加载类;
  6. 这说明我们只需要重写ClassLoader的findClass()方法,这就可以了!如果重写了loadClass()方法覆盖了代理模式!

自定义一个类加载器,只需要继承ClassLoader类,然后重写**findClass()**方法。

  • 找到class文件,把它加载到一个byte[]中
  • 调用ClassLoader类的defineClass(),传递一个byty[]进入,得到该class文件生成的Class对象
    FileSystemClassLoader
public class FileSystemClassLoader extends ClassLoader{
    private String classpath;//设置自定义类加载器负责的范围:只在这里去查找类

    public FileSystemClassLoader() {}

    public FileSystemClassLoader(String classpath) {
        this.classpath = classpath;
    }

    //通过名称找到.class文件
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        try {

            byte[] datas = getClassData(name);
            if(datas == null) {
                throw new ClassNotFoundException("类没有找到:" + name);
            }
            return this.defineClass(name, datas, 0, datas.length);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException("类找不到:" + name);
        }
    }

    //根据类名,查找到该.class文件,并加载到一个字节数组中
    private byte[] getClassData(String name) throws IOException {
        //将类名中的.转化成\,即各级文件夹名称
        name = name.replace(".", "\\") + ".class";
        //添加上查找范围
        File classFile = new File(classpath, name);
        //调用commons-io.jar工具方法,加载到字节数组中,所以需要导commons-io的jar包
        return FileUtils.readFileToByteArray(classFile);
    }
}

这里为了测试,自己写一个工具类,生成jar包,然后解压,放到如下位置
在这里插入图片描述
测试类

@Test
    public void func() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        //得到类加载器,并指定查找范围
        ClassLoader loader=new FileSystemClassLoader("G:\\ClassPath");
        //指定查找类名
        Class clazz= loader.loadClass("cn.edu.stu.tools.commons.CommonUtils");
        //通过反射,得到方法
        Method method=clazz.getMethod("uuid");
        String  uuid= (String) method.invoke(clazz);
        System.out.println(uuid);

    }

结果:

79E71E8F90D64DEF80964AAA4BB326E8

调用的方法是生成一个uuid

Tomcat类加载器

tomcat提供了两种类加载器

  • 服务器类加载器:加载${CATALINA_HOME}\lib
  • 应用类加载器:${CONTEXT_HOME}\WEB-INF\lib、${CONTEXT_HOME}\WEB-INF\classes,应用类加载器,它负责加载这两个路径下的类

但Tomcat提供的类加载器不会使用传统的代理模式,而是自己先去加载,如果加载不到,再使用代理模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值