0 背景
将技术简单叙述
1 说明
定义: 就是 java 虚拟机类加载机制
java 运行的是字节码 说的就是class, classloder 就是把class 变成内存对象的一个东西(这么理解 虽然不准确但是不要纠结)
父 class 使用的是 父classloder 加载
破坏双亲委派模型 就是用父classloder 加载子类class,产品 jdbc
2 看例子
2.1 自定义classloder
package classloder;
/**
* @author lianzheng04
* @version 1.0
* @date 2020/6/10 10:36 上午
*/
import java.io.*;
/**
* Created by zejian on 2017/6/21.
* Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
*/
public class FileClassLoader extends ClassLoader {
private String rootDir;
public FileClassLoader(String rootDir) {
this.rootDir = rootDir;
}
/**
* 编写findClass方法的逻辑
*
* @param name
* @return
* @throws ClassNotFoundException
*/
@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);
}
}
/**
* 编写获取class文件并转换为字节码流的逻辑
*
* @param className
* @return
*/
private byte[] getClassData(String className) {
// 读取类文件的字节
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
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;
}
/**
* 类文件的完全路径
*
* @param className
* @return
*/
private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
public static void func1(){
String rootDir = "/Users/lianzheng/icu/jql/sl-spring/src/main/java";
//创建自定义文件类加载器
FileClassLoader loader = new FileClassLoader(rootDir);
try {
//加载指定的class文件
Class<?> object1 = loader.loadClass("classloder.DemoObj");
System.out.println(object1.newInstance().toString());
//输出结果:I am DemoObj
} catch (Exception e) {
e.printStackTrace();
}
}
// 热部署
public static void main(String[] args) throws ClassNotFoundException {
// String rootDir = "/Users/lianzheng/icu/jql/sl-spring/src/main/java";
// 使用findclass 时候需要进行类编译成class 文件
String rootDir = "/Users/lianzheng/icu/jql/sl-spring/target/classes";
//创建自定义文件类加载器
FileClassLoader loader = new FileClassLoader(rootDir);
FileClassLoader loader2 = new FileClassLoader(rootDir);
try {
//加载指定的class文件,调用loadClass()
// Class<?> object1 = loader.loadClass("classloder.DemoObj");
// Class<?> object2 = loader2.loadClass("classloder.DemoObj");
//
// System.out.println("loadClass->obj1:" + object1.hashCode());
// System.out.println("loadClass->obj2:" + object2.hashCode());
//加载指定的class文件,直接调用findClass(),绕过检测机制,创建不同class对象。
Class<?> object3 = loader.findClass("classloder.DemoObj");
Class<?> object4 = loader2.findClass("classloder.DemoObj");
System.out.println("loadClass->obj3:" + object3.hashCode());
System.out.println("loadClass->obj4:" + object4.hashCode());
/**
* 输出结果:
* loadClass->obj1:644117698
loadClass->obj2:644117698
findClass->obj3:723074861
findClass->obj4:895328852
*/
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 破坏双亲委派机制
DriverManager.calss
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
// 这个部分加载了driver ,进入函数 loadInitialDrivers
- 加载了一个接口 ->interface 实现是厂商的实现类
- 进入 load 函数
此时 相当于 使用 当前线程的 ContextClassLoader
所以相当于 使用现在 父的classloder加载了子类的class
不摸着石头过河,难道要在温柔乡睡到天昏地暗。