类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。
类加载器分为两类:
jdk8和8之后版本的类加载器的设计差别较大,jdk8及之前的版本中默认的类加载器有如下几种:
**启动类加载器(Bootstrap ClassLoader)**是由 Hotspot
虚拟机提供的,使用C++编写的类加载器。默认加载Java安装目录/jre/lib下的类文件,比如 rt.jar,tools.jar,resources.jar等。
使用启动类加载器去加载用户jar包有两种方式:
jre/lib
目录下进行扩展
-Xbootclasspath/a:路径/jar包名.jar
进行扩展。扩展类加载器和应用程序类加载器都说JDK中提供的、使用Java编写的类加载器。它们的源码都位于 sun.misc.Launcher
中,是一个静态内部类。继承自 URLClassLoader
,可以通过目录或者指定jar包将字节码文件加载到内存中。
**扩展类加载器(Extension ClassLoader)**是jdk中提供的、使用Java编写的类加载器。默认加载Java安装目录 /jre/lib/ext
下的类文件。
通过扩展类加载器去加载用户jar包的方式:
jre/lib/ext
下进行扩展。
-Djava.ext.dirs=jar包目录
进行扩展,这种方式会覆盖掉原始目录,随意我们应该用 ;
(windows)或 :
(macos/linux)追加上原始目录。应用程序类加载器(AppClassLoader):面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。
由于JVM中有多个类加载器,双亲委派机制的核心是解决一个类到底由谁加载的问题。
双亲委派机制的作用:
java.lang.String
,确保核心类库的完整性和安全性。双亲委派机制指的是:当一个类加载器接收到加载类的任务时,会自底向上查找是否加载过,再由顶向下进行加载。
每个Java实现的类加载器中都保存了一个成员变量名为 parent
的类加载器,**可以理解为它的上级,并不是继承关系。**应用程序类加载器的parent父类加载器是扩展类加载器,而扩展类加载器的parent是空,因为启动类加载器由C++实现,无法在Java中获得。
类的双亲委派机制是什么?
打破双亲委派机制的三种方式:
loadClass
方法,就可以将双亲委派机制的代码去除。Tomcat使用了自定义类加载器来实现应用之间类的隔离。每一个应用会有一个独立的类加载器加在对应的类。
ClassLoader中包含了4个核心方法(双亲委派机制的核心代码就位于loadClass方法中):
打破双亲委派机制的关键就是重写 loadClass
方法中的逻辑。
JDBC中使用了 DriverManager
来管理项目中引入的不同数据库的驱动,比如mysql驱动,oracle驱动。
DriverManager
类位于 rt.jar 包中,由启动类加载器加载。而依赖中的mysql驱动对应的类,由应用程序类加载器来加载。这就违反了双亲委派机制。
DriverManager
使用SPI机制,最终加载jar包中对应的驱动类。
那么SPI机制是如何获取到应用程序类加载器的呢?
SPI中使用了线程上下文中保存的类加载器进行类的加载,这个类加载器一般是应用程序类加载器。
public static <S> ServiceLoader<S> load(Class<S> service){
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service,cl);
}
完整流程:
DriverManager
DriverManager
时,通过SPI机制加载jar包中的mysql驱动思考:
JDBC案例真的打破了双亲委派机制吗?
有两种说法:
DriverManager
位于rt.jar包下,由启动类加载器加载,而mysql驱动位于classpath,由应用程序类加载器加载,没有问题。历史上,OSGi模块化框架。它存在同级之间的类加载器的委托加载。OSGi还使用类加载器实现了热部署(在服务不停止的情况下,动态更新字节码文件到内存中)的功能。
答:类加载器(ClassLoader)负责在类加载过程当中获取字节码并加载到内存中转换成byte[],接下来调用虚拟机底层方法将byte[]转换成方法区和堆中的数据。
答:
答:每个Java实现的类加载器中都保存了一个成员变量叫 parent 的类加载器。自底向上查找是否加载过,再由顶向下进行加载。避免核心类被应用程序重写并覆盖的问题,提升了安全性。
答: