Java类加载器

类加载的过程:加载、连接(验证、准备、解析)、初始化。类加载器作用于类的加载阶段,可以让我们的应用程序灵活的获取类文件。
除了用于加载类,判断是否是同一个类,不仅要保证类的全限定名相同,而且还要有同一类加载器来加载。这样避免了不同来源的类产生的冲突。

一. Java的类加载器

类加载器的主要功能是将.class二进制文件加载到内存,并进行处理最后返回其对应的Class对象。
Java提供了三个基础的类加载器:BootstrapClassLoader(启动),ExtClassLoader(扩展),AppClassLoader(系统,默认类加载器)。
双亲委派机制
由于判断类是否相同时,还要对类的加载器进行判断,所以Java引进了双亲委派机制用来避免基础类由不同加载器加载引起的混乱。

  1. 加载器加载类时,首先由父加载器加载,在父加载器加载不到时,再交由子加载器加载。
  2. 类A引用了未被加载的类B,那么类B将由类A的加载器加载。

自定义类加载器
自定义加载器时,可以指定父加载器,如果不指定的话,那么就默认是AppClassLoader。并且在继承ClassLoader类后,覆写的时findClass方法,而不是loadClass方法,loadClass方法内部实现了双亲委派机制。主要逻辑是得到二进制序列,然后通过defineClass方法获取类实例。
(对已经加载的类,ClassLoader会做缓存,且加载过程会根据类的全限定名上锁)

二. SPI机制和ContextClassLoader

SPI机制:定义一套服务接口,第三方按照规范实现该接口。
SPI机制最典型的就是JDBC,JDK提供了JDBC的基本接口,交由第三方实现自己DB的Driver,另外JDK提供了DriverManager,用于管理第三方Manager,然而者带来了一个问题:
DriverManager是JDK的基础类由BootstrapClassLoader加载,而Driver的实现类是第三方类一般由AppClassLoader加载,如果由DriverManager加载Driver的实现类,由双亲委派机制可知加载不到制定的类。所以,Java引入了ContextClassLoader。

上下文类加载器
ContextClassLoader与上述三种类加载器不同,它不是ClassLoader的一种实现,而是Thread接口的一个属性,Thread提供了改属性的getter和setter方法。未set时,子线程继承父线程的类加载器,应用启动后,默认的上下文类加载器是系统类加载器(AppClassLoader)。

ContextClassLoader解决底层类加载高层类的问题
使用DriverManager时,调用方会通过Class.forName获取Driver加载类(首次加载会执行静态代码将该Driver实例注册在DriverManager上)。
接着通过DriverManager获取连接,DriverManager会加载Driver实现类,首先它获取调用者的ClassLoader,如果没有则使用上下文类加载器,然后通过这个加载器加载加载(这里还是使用的Class.forName方法),这时,DriverManager就可以操作之前注册好的Driver获取Connection。

我们看到了ContextClassLoader其实只是一个存放ClassLoader的一个位置,其实我们还可以通过其他方式存放ClassLoader,比如Tomcat就是采用的另一种机制,关于Tomcat的ClassLoader方案,且听下回分解。

附录:

  1. 类加载的时机:
    1)使用new关键字实例化对象
    2)读取一个类的静态字段(被final修饰、已在编译期把结果放在常量池的静态字段除外)
    3)设置一个类的静态字段(被final修饰、已在编译期把结果放在常量池的静态字段除外)
    4)调用一个类的静态方法
  2. Class.forName解析
    该方法可以通过传入类的全限定名,动态的加载一个类,并且还有同名方法可以指定类加载器。
    该方法常与Class的newInstance方法联合使用,
    Class t = Class.forName(str);
    t.newInstance();(与new的区别是,只能调用无参构造函数)

参考文献:
https://blog.csdn.net/sigangjun/article/details/79071850(Java的SPI机制)
https://blog.csdn.net/sushauai/article/details/50879704(JDBC机制)

你可能感兴趣的:(Java类加载器)