JVM(二)、常见的类加载器

类加载器ClassLoader:在装载(Load)阶段,其中第一步就是通过类的全限定名获取其定义的二进制字节流,需要借助类加载器来完成

所以,说到底,类加载器就是用来装载Class文件的;

一、常见的类加载器:

如上图:其实常见的类加载器无非就那么几种:

BootstrapClassLoader、ExtensionClassLoader、APPClassLoader、CustomClassLoader

  • Bootstrap ClassLoader:负责加载 JAVA_HOME中 jre/lib/rt.jar 里所有的class或Xbootclassoath选项指定的jar包。由C++实现,不是ClassLoader子类。
  • Extension ClassLoader:负责加载java平台中扩展功能的一些jar包,包括`$$JAVA_HOME中jre/lib/*.jar 或 -Djava.ext.dirs指定目录下的jar包。
  • App ClassLoader:负责加载classpath中指定的jar包及 Djava.class.path 所指定目录下的类和jar包。
  • Custom ClassLoader:通过java.lang.ClassLoader的子类自定义加载class,属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。

二、什么是双亲委派

其实关于类加载器相关的,就不得不提到另外一个概念,也就是双亲委派

其实双亲委派是我们音译过来的叫法,严格意义上来说叫做 父类委派 更合适一些;

按照个人理解(个人理解,如有错误,还请指正)通俗的解释下双亲委派的流程:

在进行类加载的过程,首先其实就是会执行loadClass方法,一句话概括其实就是向上检查,向下委派;

如上图,在开始进行类加载时,会传入类的全限定名(就是包名+类名),先从APPClassLoader开始检查当前类加载器是否已经加载过此类,如果已经加载过,那就不会再次加载,当前类的加载过程就结束了,如果没有加载过,那就找APPClassLoader的父级类加载器,同理会做出相同的判断,如果没有,继续向父级类加载器做检查,直到判断出现父级类加载器为空,说明已经到了顶层的类加载器,如果还是没有加载过当前类,那么就开始从顶层做第二个判断,判断当前类加载器是否可以加载当前类,如果可以,那就直接加载,如果不可以,那就委派给其子类的类加载器,以此类推,如果知道最后一层判断,还是不能加载当前类,那么就会抛出ClassNotFountException异常信息;

三、打破双亲委派

1、为什么要打破双亲委派

然而,在某些情况下,我们可能需要打破双亲委派模型。例如,在Web容器中,如Tomcat,需要部署多个应用,每个应用可能会出现相同的类名或者引用相同的jar包但版本不同。这种情况下,如果使用双亲委派模型,一个类只会加载一次,可能会导致项目之间有冲突。因此,为了实现各自的应用加载自己应用的类,互不影响,就需要打破双亲委派模型。

2、打破双亲委派的方式

  1. 复写:自定义一个类加载器,复写loadClass方法,自定义类加载的过程
  2. SPI(Service Provider Interface):SPI机制的核心是定义一个接口,然后由第三方提供实现类,这些实现类可以通过配置文件来注册。应用程序在运行时可以通过加载配置文件来发现这些服务
  3. OSGI:热更新、热部署 每个模块都有自定义的ClassLoader,直接把原来的ClassLoader卸载掉,然后再换上新的;

下面为一个自定义的类加载器的事例(内部需要定义自己的业务逻辑!):

 public class MyClassLoader extends ClassLoader {  
    private String resourcePath;  
  
    public MyClassLoader(String resourcePath) {  
        this.resourcePath = resourcePath;  
    }  
  
    @Override  
    public Class<?> findClass(String name) throws ClassNotFoundException {  
        try {  
            InputStream is = getResourceAsStream(name.replace(".", "/") + ".class");  
            if (is == null) {  
                throw new ClassNotFoundException("Class " + name + " not found");  
            }  
            byte[] bytes = new byte[is.available()];  
            is.read(bytes);  
            return defineClass(name, bytes, 0, bytes.length);  
        } catch (IOException e) {  
            throw new ClassNotFoundException("Error reading class from stream", e);  
        }  
    }  
  
    @Override  
    public InputStream getResourceAsStream(String name) {  
        return this.getClass().getClassLoader().getResourceAsStream(this.resourcePath + "/" + name);  
    }  
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆包侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值