java中的类加载器

文章详细介绍了Java中的类加载器,包括引导加载器、扩展加载器、应用加载器以及线程上下文类加载器。重点讨论了双亲委派机制的作用,防止类的重复加载和保证核心类的安全。同时,解释了为何有时需要打破双亲委派,如Spring框架中的动态加载需求,实现热插拔功能。
摘要由CSDN通过智能技术生成


前言,

java中一般来说有三种类加载器,分别为: 引导加载器,扩展加载器,应用加载器,还有一个线程上下文类加载器


一、加载器的作用是什么

使Java 类可以被动态加载到 Java 虚拟机中并执行;

  • 这样动态加载的的好处是什么?
    可以实现热插拔式的编程;

举个栗子:
springdata中支持多种数据源,例如mysql;但是当我不去配置mysql的yml的时候,它就不会去加载它,也不会报错;当我配置了yml的时候,它就开始验证驱动包是否存在,用户名密码是否正确了
但是自始至终springdata的相关自动配置都是在的,仅仅是没有被加载而已,这就是插件式;

二、详解类加载器

1.不得不说的双亲委派机制

双亲委派机制

例如加载HelloWorld的时候的类加载过程:
1、寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个Bootstrap Loader(启动类加载器);
3、Bootstrap Loader自动加载Extended Loader(标准扩展类加载器),并将其父Loader设为Bootstrap Loader。
4、Bootstrap Loader自动加载AppClass Loader(应用/系统类加载器),并将其父Loader设为Extended Loader。
5、最后由AppClass Loader加载HelloWorld类。

用大白话说就是: 应用类加载器为最底层,当加载一个类的时候,一般从应用类加载器开始加载,但是每次他都不是直接加载的,而且去父类中找下,看是不是已经加载了,加载了的话,就结束了,应用类加载器就不加载饿了,他的父类: 扩展类加载器也做了同样操作;

双亲委派机制的作用是什么?

①防止加载同一个.class。通过委托去询问上级是否已经加载过该.class,如果加载过了,则不需要重新加载。保证了数据安全。
②保证核心.class不被篡改。通过委托的方式,保证核心.class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。

第一个点说的已经很清楚了,第二点我这里解析下
例如: 我自己新建了一个String.java,它与java中的是同名的一个类,这样的话,双亲委派机制可以保证,它不会被加载,如果被加载,相当于篡改了原java对象;其次就算加载了,问题也不是太大,因为默认的java基类,都是通过启动类加载器加载的,而我们自己的写的文件,通常是通过应用加载器加载的,所以也可以保证原java基类的安全;

2.各个加载器加载的内容

1.BootstrpLoader
BootstrpLoader是一个启动类加载器,是Java类中最顶层的类加载器,常用于加载JDK中的核心类库,如rt.jar、resources.jar、charsets.jar等。

2.ExtClassLoader
ExtClassLoader是一个扩展类加载器,常用于加载java的扩展类库,可以加载JAVA_HOME/jre/lib/ext/目录中的所有jar。

3.AppClassLoader
AppClassLoader是一个系统类加载器,是java程序的默认类加载器,常用于加载classpath所指定位置的类或jar包,可以加载java中的classpath或java.class.path系统属性。

3.线程上下文类加载器

它的出现打破了之前提到的双亲委派机制,就是原来 自己先不加载而是去父类中找,相当于委托给父类去加载;
打破之后变为: 原本应该是父类加载,但是由于加载不到,所以父类委托了子类, 应用加载器去加载,委托关系反过来了,应用类加载器加载成功,就ok了,如果也加载不到 直接排除异常 java.lang.ClassNotFoundException;

  • 打破双亲委派机制得原因

例如: 加载mysql驱动
在加载第三方类库时,加载Driver类的实现类时,一开始就不是系统类加载器去加载的,而是直接使用启动类加载器进行加载
因为本身这个类就是java 基类中提供的,它仅仅是去找实现类而已;但是它找不到,因为java基类只提供了接口标准,没有提供实现类
由于驱动仅仅是一个标准,由各个厂商实现,需要运行时动态获取,那么启动类无法加载,最终能加载的就只能是应用类加载器
相当于之前的双亲委派模式顺序变了,父类先尝试加载未加载成功,然后子类去加载了
但他不是直接去找了子类,而是通过一个叫做线程上下文类加载器,但这个类加载器,实际上就是应用类加载器

  • 打破双亲委派机制的目的

在某些情况下,需要跳出双亲委派机制,以便自定义类加载器可以加载某些特定的类,从而实现某些特定的功能。Spring框架就是一个典型的例子,它需要跳出双亲委派机制,以便在某些特定的情况下,自定义类加载器可以加载Spring的某些特定的类,从而实现Spring的一些特定的功能。
例如: springboot 中yml 我没有mysql的相关配置,那么就不会报错,当我写入了mysql配置,开始验证驱动包是否存在,用户名密码是否正确~,这也是springboot实现热插拔的主要手段
当多个版本的jar共存的时候,例如 jdk版本,类全路径都是一样的,但是版本不通,一台机器上就是需要两个版本共用,且各自隔离;
打破双亲委派机制的目的

4.类加载器的庐山真面目

  • 这就是四个类加载器了
    	 //扩展类加载器Main
        ClassLoader classLoader = Abc.class.getClassLoader();
        //表示当前线程的类加载器——应用程序类加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        //—启动类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        // 启动类加载器
        ClassLoader application = String.class.getClassLoader();
        
  • 开图验证~
    类加载器

1 从图中可以看出,我们是利用当前的应用加载器一直向上寻找父级
2 可以看到while循环经过了两次就停止了,说明找到了扩展类加载器
3 当扩展类加载器再去找父级的时候,启动类加载器为null
4 再次验证下,利用String.class,再次找一次启动类加载器,仍然为null
5 这是由于默认被隐藏了,不对外暴露,实际上是存在的


总结

个人理解:

  1. 类加载器可以动态将一些类加载的虚拟机中,与sping中的ioc类似;
  2. spring中就是利用线程上下文类加载器,不停的判断是否需要加载某些类,卸载,装载这些类,从而实现了热插拔,其实就是spi机制;
  3. spring中的 spring.factories 或者是 spring-configuration-metadata.json 同样也是spi的一种实现,与原始java中的spi异曲同工;

有错误之处,请指教,相互学习;

参考文章:

  1. 双亲委派机制
  2. 为何要打破双亲委派机制
  3. 四次打破双亲委派机制的原因
  4. 线程类加载器
  5. 打破双亲委派的验证
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

寂寞旅行

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

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

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

打赏作者

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

抵扣说明:

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

余额充值