类加载器
基本介绍(基于jdk1.8)
名称 | 加载哪的类 | 说明 |
---|---|---|
BootStrap ClassLoader(启动类加载器) | JAVA_HOME/jre/lib | 无法直接访问(C++代码书写的) |
Extension ClassLoader(扩展类加载器) | JAVA_HOME/jre/lib/ext | 上级为BookStrap,显示为null |
Application ClassLoader(应用程序类加载器) | classpath | 上级为Extension |
自定义类加载 | 自定义 | 上级为Application |
启动类加载器:
- -Xbootclasspath表示设置bootclasspath
- 其中/a:.将当前目录追加至bootclasspath之后
- 可以用这个方法替换核心类
- java -Xbootclasspath:<new bootclasspath>{解释:用新路径完全替换掉了原来路径JAVA_HOME/jre/lib}
- java-Xbootclasspath/a:<追加路径>{解释:不替换原有路径,在原有路径后追加路径a}
- java- Xbootclasspath/p:<追加路径 >{解释:在原有路径上前追加}
扩展类加载器
- 扩展类加载器下的类必须以jar包的方式存在
双亲委派模式
-
总体思想:委派上级优先做类的加载,上级没有此类,再由本级的类加载器进行加载
-
执行流程:
调用本类的classLoad方法:
先检测该类在本类加载器中是否已加载(findLoadedClass(name)),
若没有,再判断此加载器是否存在上级。
若有上级,委派上级:调用上级类加载器的classLoad方法;
若不存在上级,证明此类加载器为扩展类加载器,则调用findBootstraporNull(name)方法来完成类的查找
若所有的上级都没有找到此类;
则在本级查找此类,找到:本类加载器调用findClass(name)方法(每个类加载器自己扩展的)来加载这个类。找不到:抛出类找不到异常。
-
注意:
- 这里的双亲委派,就是调用类加载器的loadclass方法时,查找类的规则
- 这里的双亲指的是上级,他们并没有继承关系。
线程上下文类加载器
前提:在使用jdbc时,要加载Driver驱动(Class.forName("com.mysql.jdbc.Driver")),不写它时,此驱动也是会被正常加载的。
问题提出:DriverManager(java.sql.DriverManager)是启动类路径下的,故它的类加载器是Bookstrap ClassLoader,会到JAVA_HOME/jre/lib下搜索类,显然此路径下是没有mysql-connector-java-5.1.48.jar包的。
那DriverManager在静态代码块中怎样正确的加载com.mysql.jdbc.Driver呢?
加载方法:
打破了双亲委派的模式,使用应用程序类加载器启动类的加载
- 使用ServiceLoader机制加载驱动,即SPI ------> 使用应用程序类加载器启动类的加载,
- 使用jdbc.drivers定义的驱动名加载驱动 ------> 使用应用程序类加载器启动类的加载
体现了面向接口编程+解耦的思想
注:线程上下文类加载器是当前线程使用的类加载器,
在线程启动时,由jvm默认将应用程序类加载器赋值给当前线程。
自定义类加载器
- 需要自定义类加载器的情况?
- 想加载非classpath随意路径中的文件
- 都是通过接口来使用实现,希望解耦时常用在框架设计
- 这些类希望予以隔离,不同应用的同名类都可以加载,不冲突,常见于tomcat容器
- 步骤:
- 继承ClassLoader类
- 要遵循双亲委派机制,重写findClass()方法
- 注意:不是重写loadClass方法,否则不会走双亲委派机制
- 读取类文件的字节码
- 调用父类的defineClass方法来加载类
- 使用者调用该类加载器的loadClass方法