一、类加载器及加载范围
对于虚拟机,只有两种不同的类加载器:
1.启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;
2.其它所有的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全部继承自java.lang.ClassLoader。
具体划分,如下:
1.启动类加载器(Bootstrap ClassLoader):这个类加载器负责将放置在
2.扩展类加载器(Extension ClassLoader):这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载
3.应用程序类加载器(Application ClassLoader):这个类加载器由sum.misc.Launcher.$AppClassLoader来实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也被称为系统类加载器。它负责加载用户类路径上所指定的类库(classPath),开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
应用程序由这三种类加载器互相配合进行加载的,如果有必须,还可以加入自己定义的类加载器。这些类加载器之间的关系一般如下图:
文章图片
上图中展示的类加载器之间的层次关系,就称为类加载器的双亲委派模型(Parents Delegation Model)。双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不会以继承的关系来实现,而是使用组合关系来复用父加载器的代码。
二、双亲委派模型
双亲委派模型的加载过程:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完全这个加载请求时(查看该类是否是在自己的加载范围),子加载器才会尝试自己去加载。
双亲委派的好处:
1.安全性:使用双亲委派模型来组织类加载器之间的关系,有一个很明显的好处,就是Java类随着它的类加载器(说白了,就是它所在的目录)一起具备了一种带有优先级的层次关系,这对于保证Java程序的稳定运作很重要。例如,类java.lang.Object类存放在JDK\jre\lib下的rt.jar之中,因此无论是哪个类加载器要加载此类,最终都会委派给启动类加载器进行加载,这边保证了Object类在程序中的各种类加载器中都是同一个类,可以防止java核心类被篡改。
2.避免重复加载:当使用了双亲委派机制后,当一个类已经被上级类加载器加载后,下级类加载器就不会出现重复加载的情况。
java类加载机制:
https://www.cnblogs.com/200911/p/9006822.html
三、破坏双亲委派模型
双亲委派模型很好的解决了各个类加载器加载基础类的统一性问题。即越基础的类由越上层的加载器进行加载。
若加载的基础类中需要回调用户代码,而这时顶层的类加载器无法识别这些用户代码,怎么办呢?这时就需要破坏双亲委派模型了。
下面说说常用的破坏双亲委派模型的例子:
1、JNDI破坏双亲委派模型
JNDI是Java标准服务,它的代码由启动类加载器去加载。但是JNDI需要回调独立厂商实现的代码,而类加载器无法识别这些回调代码(SPI)。
为了解决这个问题,引入了一个线程上下文类加载器。 可通过Thread.setContextClassLoader()设置。
利用线程上下文类加载器去加载所需要的SPI代码,即父类加载器请求子类加载器去完成类加载的过程,而破坏了双亲委派模型。
【Java类加载器及加载范围、双亲委派模型及破坏双亲委派模型】具体涉及到的代码可以看看这个,写的非常详细:https://www.jianshu.com/p/09f73af48a98
2、Spring破坏双亲委派模型
Spring要对用户程序进行组织和管理,而用户程序一般放在WEB-INF目录下,由WebAppClassLoader类加载器加载,而Spring由Common类加载器或Shared类加载器加载。
那么Spring是如何访问WEB-INF下的用户程序呢?
使用线程上下文类加载器。 Spring加载类所用的classLoader都是通过Thread.currentThread().getContextClassLoader()获取的。当线程创建时会默认创建一个AppClassLoader类加载器(对应Tomcat中的WebAppclassLoader类加载器): setContextClassLoader(AppClassLoader)。
利用这个来加载用户程序。即任何一个线程都可通过getContextClassLoader()获取到WebAppclassLoader。
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)