双亲委派模型
- 双亲委派工作机制
- 双亲委派的作用
- 双亲委派的实现源码
- SPI打破双亲委派
应用程序是由三种类加载器相互配合,从而实现类加载,除此之外还可以加入自己定义的类的加载器。
类加载器之间的层次关系,称为双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其他的类加载器都要有自己的父类加载器。这里的父类关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。
双亲委派工作机制
一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时菜尝试自己加载。
双亲委派的作用
- 每个类只会加载一次,解决了各个类加载器加载基础类的统一问题(基础类库由上层的加载器进行加载)
- 防止恶意破坏的类加载,内存中不会出现多份同样的字节码的系统类iii,保证Java程序安全稳定运行。
例如:java.lang .object存放在rt.jar中,如果编写另外—个java.lang.Object并放到ClassPath 中,程序可以编译通过。由于双亲委派模型的存在,所以在rt.jar中的Object比在ClassPath 中的 Object 优先级更高,因为rt.jar中的object使用的是启动类加载器,而ClassPath中的 object使用的是应用程序类加载器。rt.jar中的Object优先级更高,那么程序中使用的所有的 Object都是由启动类加载器所加载的Object 。
双亲委派的实现源码
以下是抽象类 java.lang.ClassLoader的代码片段,其中的loadClass()方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 ClassNotFoundException,此时尝试自己去加载。
public abstract class ClassLoader{
//The parent class loader for delegation
private final ClassLoader parent;
public Class<?> loadClass(String name) throws ClassNotFoundException{
return loadClass(name,false);
}
protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException{
synchronized (getClassLoadingLock(name)){
//First,check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if(c == null){
try{
if (parent != null){
c = parent.loadClass(name,false);
}else{
c = findBootstrapClassOrNull(name);
}
}catch (ClassNotFoundException e){
//ClassNotFoundException thrown if class not foundl from the non-null parent class loader
}
if (c == null){
// If still not found,then invoke findclass in orderll to find the class.
c = findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}
}
protected Class<?> findClass(String name) throws ClassNotFoundException{
throw new ClassNotFoundException(name);
}
}
SPI打破双亲委派
SPI(Service Provider Interface),是一种服务发现机制,它通过在ClassPath路径下的META-INF/ services文件夹查找文件,自动加载文件里所定义的类。
如下图,SPI核心类定义在rt.jar中(例如:java.lang.Driver接口),所以java.lang.Driver接口本身是由启动类加载器加载,当调用java.lang.Driver接口的实现类时,启动类加载器是无法加载实现类的,这个时候就提供了线程上下文类加载器(Thread context ClassLoader )加载实现类,ThreadcontextClassLoader是可以通过 java.lang.Thread #setcontextclassLoader方法设置类加载器,这样就打破了双亲委派的类加载模式。