当前位置:   article > 正文

双亲委派机制 Parent Delegation Model_双亲委派用的什么设计模式

双亲委派用的什么设计模式

当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。

如果都无法加载,就会触发findclass,抛出classNotFoundException.

按照加载器的层级关系,逐层进行委派。

不是所有的类加载都遵守这个模型,有的时候,启动类加载器所加载的类型,是可能要加载用户代码的,比如JDK内部的ServiceProvider/ServiceLoader机制,用户可以在标准API框架上,提供自己的实现,JDK也需要提供默认的参考实现。例如,Java中的 JNDI、JDBC、文件系统、Cipher 等很多方面,都是利用的这种机制,这种情况就不会用双亲委派模型去加载,而是利用所谓的上下文加载器。

1.作用

双亲委派机制,避免了类的重复加载,避免了核心类库被修改

保护了程序的安全性,防止核心的API被修改。

采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改

2.类加载器

类加载机制:编译器把Java源文件编译成.class文件,再由JVM装载.class文件到内存中,JVM装载完成后得到一个Class对象字节码。有了字节码对象,接下来就可以实例化使用了。

作用:

隔离加载类

在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。

修改类加载的方式

类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点按需进行动态加载。

扩展加载源

比如从数据库、网络、电视机机顶盒进行加载

防止源码泄漏

Java代码容易被编译和篡改,可以进行编译加密,那么类加载也需要自定义,还原加密的字节码。

2.1类加载过程

加载:将字节码文件通过IO流读取到JVM的方法区,并同时在堆中生成Class对像。

验证:校验字节码文件的正确性。

准备:为类的静态变量分配内存,并初始化为默认值;对于final static修饰的变量,在编译时就已经分配好内存了。

解析:将类中的符号引用转换为直接引用。

初始化:对类的静态变量初始化为指定的值,执行静态代码

2.2类加载的分类

class文件的显式加载隐式加载是指JVM加载class文件到内存的方式。

显式加载指的是在代码中通过调用ClassLoader加载class对象,如直接使用Class.forName(name)或this.getClass().getClassLoader().loadClass()加载class对象。

隐式加载则是不直接在代码中调用ClassLoader的方法加载class对象,而是通过虚拟机自动加载到内存中,如在加载某个类的class文件时,该类的class文件中引用了另一个类的对象,此时额外引用的类将通过JVM自动加载到内存中。

自定义类加载器

Java开发者可以自定义类加载器来实现类库的动态加载。加载源可以是本地的JAR包,也可以是网络上的远程资源。

类加载器为应用程序提供了一种动态增加新功能的机制,这种机制无需重新打包发布应用程序就能实现。

Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类。

在自定义ClassLoader子类时,我们常见的会有两种做法:

1.重写loadClass()方法 2.方式二:重写findClass()方法 → 推荐

2.四种加载器(逐级向下 父到子)

引导类加载器 Bootstrap ClassLoader(启动类加载器) :主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。

Extention ClassLoader(扩展类加载器):主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。

java1.9 扩展类加载器(Extension Class Loader)被平台类加载器(Platform ClassLoader)取代

在委派给父加载器加载时,先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载

Application ClassLoader(应用程序类加载器) :主要负责加载当前应用的classpath下的所有类

User ClassLoader(用户自定义类加载器) : 用户自定义的类加载器,可加载指定路径的class文件

上下文加载器

它可以打破双亲委托机制

类Thread中的getContextClassLoader()和setContextClassLoader(ClassLoader cl)分别用来获取和设置上线文类加载器。

对于SPI来说,有些接口是Java核心库所提供的,而Java核心库是由启动类加载器加载的,而这些接口的实现却是来自于不同jar包(厂商提供),Java的启动类加载是不会加载其他来源的jar包,这样传统的双亲委托模型就无法满足SPI的要求。而通过给当前线程设置上下文类加载器,就可以由设置的上线文类加载器来实现与接口实现类的加载。

如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承其父线程的上下文类加载器。

Class.forName()加载用的是调用者的Classloader,这个调用者DriverManager是在rt.jar中的,ClassLoader是启动类加载器,而com.mysql.jdbc.Driver肯定不在<JAVA_HOME>/lib下,所以肯定是无法加载mysql中的这个类的。这就是双亲委派模型的局限性了,父级加载器无法加载子级类加载器路径中的类。

这个mysql的drvier只有应用类加载器能加载,那么我们只要在启动类加载器中有方法获取应用程序类加载器,然后通过它去加载就可以了。这就是所谓的线程上下文加载器

热替换(插件热插拔

任何一个使用到的Class,都需要被classLoader加载到JVM中,这个加载的过程,又分为

defineClass

loadClass

resolveClass

即根据提供的不同形式的class文件的内容,转化成一个Class,对其进行加载,链接。

而所谓的hotswap,就是使用新的代码替换掉已经加载的这个Class中的内容。

热替换是指在程序的运行过程中,不停止服务,只通过替换程序文件来修改程序的行为。

热替换的核心就在于Instrumentation的两个方法:

addTransformer()用来注册类的修改器;

retransformClasses()会让类重新加载,从而使得注册的类修改器能够重新修改类的字节码。

沙箱安全机制

将java代码限定在虚拟机(jvm) 特定的运行范围中,并且严格限制代码对本地系统资源的访问,通过这样的措施来保证对代码的有效隔离,防止对系统造成破坏。

3.流程

1.首先判断该类是否已经被加载

2.该类未被加载,如果父类不为空,交给父类加载

3.如果父类为空,交给bootstrap classloader 加载

  1. 如果类还是无法被加载到,则触发findclass,抛出classNotFoundException(findclass这个方法当前只有一个语句,就是抛出classNotFoundException),如果想自己实现类加载器的话,可以继承classLoader后重写findclass方法,加载对应的类)

4.破坏双亲委派机制

受到加载范围的限制,父类加载器无法加载到需要的文件

线程上下文类加载器可以通过Thread.setContextClassLoaser()方法设置,如果不特殊设置会从父线程继承,一般默认使用的是应用程序类加载器。 很明显,线程上下文类加载器让父级类加载器能通过调用子级类加载器来加载类,这打破了双亲委派模型的原则

在JDBC4.0以后,开始支持使用spi的方式来注册这个Driver,具体做法就是在mysql的jar包中的META-INF/services/java.sql.Driver 文件中指明当前使用的Driver是哪个,然后使用的时候就直接这样就可以了

实现是由服务商提供的,由系统类加载器加载,这个时候就需要破坏了双亲委派, 启动类加载器来委托子类加载器来加载Driver实现。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/194051
推荐阅读
相关标签
  

闽ICP备14008679号