类加载器
三层类加载器
1.启动类加载器-BootstrapClassLoader
AppClassLoader负责加载核心类,存放在lib目录下的jar包或class文件。
2.扩展类加载器-ExtensionClassLoader
ExtensionClassLoader负责加载\lib\ext目录下的jar包或class文件,我们可以将通用性的功能,打成jar包放置到ext
3.应用程序加载器-ApplicationClassLoader
ApplicationClassLoader负责加载用户类路径的所有类库,比如classpath就是由ApplicationClassLoader加载的。
他们三者关系如图所示:
双亲委派机制
工作机制
如果一个类加载器收到了类加载的请求,它会把这个请求委派给父类加载器去完成,每层的类加载都是这个处理逻辑,如果父加载器反馈自己无法完成的时候,子加载类就会自己去尝试加载。
为什么这样设计一个机制
其实是为了保证基础类库的稳定性,安全性。基础类rt.jar
有很多我们开发中经常用到的类比如String
类,假设没有设计这个机制,我们在项目中同样声明一个String类那岂不是被替换掉了。由此可以看出,此机制设计的目的在于保证基础类库的稳定性和安全性。
学习源码
//java.lang.ClassLoader
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//1.首先,检查这个类是否被加载了Class<?> c = findLoadedClass(name);if (c == null) {//2.当前这个类未被加载//2.1 让上层去负责加载类long t0 = System.nanoTime();try {if (parent != null) {//如果存在上层的类加载器//就交给父加载器去加载c = parent.loadClass(name, false);} else {//如果不存在上层的类加载器//就交给BootstrapClassLoader来负责加载c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.//2.1 说明父加载器未加载到类long t1 = System.nanoTime();//交给自己来加载c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}
自定义类加载器
1.代码构思
建好一个Order类,然后在Main方法中new一个Order类。在项目文件夹中找到Order.class文件。复制到这个路径:G:\com\classLoader\Order.class,然后删掉Order.java文件,因为项目中的类会优先使用ApplicationClassLoader去加载,达不到我们自定义类加载的效果。
- 继承java.lang.ClassLoader
- 重写findClass方法
- 自定义一个方法,通过自定义类加载器,加载磁盘上的class文件
注意:包名要保持一致,测试代码中的包名要和磁盘中的包名保持一致。要不然会报错
2.代码实现
package com.classLoader;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;public class MyClassLoader extends ClassLoader{//指定从哪加载类private String classPath;public MyClassLoader(String classPath){this.classPath = classPath;}@Overrideprotected Class<?> findClass(String className) throws ClassNotFoundException {//1.根据类全名获取到对应的字节数组byte[] data = loadByte(className);//2.通过JDK提供的API,将字节数组转换为Classs对象return defineClass(className,data,0,data.length);}private byte[] loadByte(String className) {//1.将类全名转换为完整类路径className = className.replaceAll("\\.","/");//G:\javaHomeWork\jvmDemo\src\main\java\com\Demo\com.classLoaderStringBuilder stringBuilder = new StringBuilder(classPath);stringBuilder.append(className);stringBuilder.append(".class");System.out.println(stringBuilder);//2.采用文件字节流读取文件内容,并将其转换为字节数组FileInputStream inputStream = null;try {inputStream = new FileInputStream(stringBuilder.toString());//3.创建字节数组,用于存放文件内容byte[] data = new byte[inputStream.available()];inputStream.read(data);return data;} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {if(inputStream!=null){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}return null;}
}
3.测试代码
package com.classLoader;public class MyClassLoaderTest {public static void main(String[] args) throws ClassNotFoundException {MyClassLoader myClassLoader = new MyClassLoader("G:/");Class<?> clazz = myClassLoader.loadClass("com.Person");//AppClassLoader MyClassLoaderSystem.out.println(clazz.getClassLoader());System.out.println(myClassLoader.getParent());}
}
4. 运行结果
5. 自定义类加载器关系
6. 打破双亲委派机制
我们在不删除Order类的情况下去使用我们的自定义类加载器去加载Order类
思路如下:
- loadClass方法规定了双亲委派机制的工作模式
- 只需要重写loadClass方法
代码实现
@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {if (name.startsWith("com.classLoader")) {//如果是我们自己项目自定义的类,则交给自己来加载c = findClass(name);}else{//交给父类加载器去加载//保证核心类库的唯一性c = this.getParent().loadClass(name);}}if (resolve) {resolveClass(c);}return c;}}
运行结果
7.Tomcat的类加载机制
tomcat如何保证每个应用的类库的独立的?
不同的类加载器实例加载的类是隔离的
tomcat为每个web项目创建一个类加载实例,webappClassLoader