双亲委派模型-java自定义类加载器
Posted 信行合一
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双亲委派模型-java自定义类加载器相关的知识,希望对你有一定的参考价值。
JVM预定义的三种类型类加载器:
首先classloader 分三个级别,最上级: bootstrap classLoader 中间级: extension classLoader 最低级: app classLoader.
1.启动(Bootstrap)类加载器:
是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib
下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
2.标准扩展(Extension)类加载器:
是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext
或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
3.系统(System)类加载器:
是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)
中指定的类库加载到内存中。开发者可以直接使用系统类加载器。
类加载过程:
当需要加载某个类的时候,会看看这个类是否已经被加载了,如果没有,1.会请求app 级来加载,2.app 请求 extension 级 3.extension 请求 bootstrap级, 由最高级来负责加载(这个就是双亲委派,委托 上两级的loader来做加载,所以叫双亲委派模型),如果高级的无法加载 则会将人物返回给 下一级 以此类推 最后如果双亲都不行 就由自己来加载。
为什么要用这个机制?
1.增加安全性
比如 java.lang.String 这个类,这个是jdk提供的类, 如果我们自定义个 包名:java.lang 然后在里面创建一个String 类, 当我在用String类的时候,根据前面所说,是由bootstrap级的loader 来进行加载的,这个时候它发现其实已经加载过了jdk的String了,那么就不会去加载自定义的String了,因为程序在创建一个类型之前,首先去缓存中查找是不是已经存在这个自定义的String了,如果存在了就不会再去加载了。
2.防止重复加载: 防止内存中出现多份同样的字节码
比如 java.lang.String 这个类,在很多地方都会用到,如果每一个使用到的地方都重新加载一次的话,系统中就会出现多分 java.lang.String 的实例,就会导致实例的重复。
4.线程上下文类加载器:
除了以上列举的三种类加载器,还有一种比较特殊的类型 — 线程上下文类加载器,这个类加载器默认使用的是应用类加载器,线程上下文加载器是打破了双亲加载模型的类加载器。之所以这么说是因为线程上下文可以通过getContextClassLoader()和 setContextClassLoader(ClassLoader cl)方法来获取和设置线程的上下文类加载器,设置类加载器是为了实现自己特殊的一些业务,比如 tomcat 的jar包存在于lib, common,shared 这样好几个不同的目录下,而系统自带的类加载器无法加载这些类,因为他们都有指定的默认加载路径(详情见上面 1,2,3,启动、标准、系统类加载器解释),而其他位置的类路径它们无法加载,因此这个时候就需要自定义加载器来实现特殊类的加载了。
System.out.println(Thread.currentThread().getContextClassLoader());
结果:
sun.misc.Launcher$AppClassLoader@14dad5dc
5.自定义类加载器
类加载器与类的唯一性:
类加载器虽然只用于实现类的加载动作,但是对于任意一个类,都需要由加载它的类加载器和这个类本身共同确立其在Java虚拟机中的唯一性。通俗的说,JVM中两个类是否“相等”,首先就必须是同一个类加载器加载的,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要类加载器不同,那么这两个类必定是不相等的。
这里的“相等”,包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用instanceof关键字做对象所属关系判定等情况。
以下代码说明了不同的类加载器对instanceof关键字运算的结果的影响:
文件类加载器:
package loadertest;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
public class FileClassLoader extends ClassLoader
private String rootDir;
public FileClassLoader(String rootDir)
this.rootDir = rootDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
// 获取类的class文件字节数组
byte[] classData = getClassData(name);
if (classData == null)
throw new ClassNotFoundException();
else
//直接生成class对象
return defineClass(name, classData, 0, classData.length);
private byte[] getClassData(String className)
String path = classNameToPath(className);
try
InputStream ins = new FileInputStream(path);
ByteOutputStream baos = new ByteOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
// 读取类文件的字节码
while ((bytesNumRead = ins.read(buffer)) != -1)
baos.write(buffer, 0, bytesNumRead);
return baos.toByteArray();
catch (IOException e)
e.printStackTrace();
return null;
private String classNameToPath(String className)
return rootDir + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
public static void main(String[] args) throws ClassNotFoundException, MalformedURLException
String rootDir="D:\\\\workspaces\\\\idea\\\\spring-learning\\\\target\\\\classes";
//创建自定义文件类加载器
FileClassLoader loader = new FileClassLoader(rootDir);
FileClassLoader loader1 = new FileClassLoader(rootDir);
try
//加载指定的class文件
Class<?> object1=loader.findClass("loadertest.DemoObj");
Class<?> object2=loader1.findClass("loadertest.DemoObj");
System.out.println(object1);
System.out.println(object2);
System.out.println(object1.hashCode());
System.out.println(object2.hashCode());
System.out.println(object1 == object2);
//输出结果:I am DemoObj
catch (Exception e)
e.printStackTrace();
打印结果:
class loadertest.DemoObj
class loadertest.DemoObj
1229416514
2016447921
false
URL 类加载器:
package loadertest;
import java.io.File;
import java.net.*;
public class FileUrlClassLoader extends URLClassLoader
public FileUrlClassLoader(URL[] urls, ClassLoader parent)
super(urls, parent);
public FileUrlClassLoader(URL[] urls)
super(urls);
public FileUrlClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory)
super(urls, parent, factory);
public static void main(String[] args) throws ClassNotFoundException, MalformedURLException
String rootDir="D:\\\\workspaces\\\\idea\\\\spring-learning\\\\target\\\\classes";
//创建自定义文件类加载器
File file = new File(rootDir);
//File to URI
URI uri=file.toURI();
URL[] urls=uri.toURL();
FileUrlClassLoader loader = new FileUrlClassLoader(urls);
try
//加载指定的class文件
Class<?> object1=loader.findClass("loadertest.DemoObj");
System.out.println(object1.newInstance());
//输出结果:I am DemoObj
catch (Exception e)
e.printStackTrace();
以上是关于双亲委派模型-java自定义类加载器的主要内容,如果未能解决你的问题,请参考以下文章