上文:jdk-Launcher源码学习
背景
上文说过,jdk是通过双亲委派机制实现类的加载,但是这个加载效率及场景存在弊端,所以本文借鉴tomcat的实现方式去打破双亲委派机制实现自定义类加载器来模似tomcat的类加载方式。
实现方式
实现思想:首先继承ClassLoader,然后通过classLoader进行重写findClass实现。
代码语言:javascript复制package com.classloader.simulate;
import com.encryption.demo.EncryptionUtils;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author hong
* @version 1.0
* @description: 模似打破双亲委派机制
* @date 2022/4/16 18:50
*/
public class SimulateTomcatClassLoader {
static class MySelfClassLoader extends ClassLoader{
/**
* 类路劲
**/
private String classPath;
public MySelfClassLoader(String classPath) {
this.classPath = classPath;
}
private byte[] loadByClassName(String className) throws Exception {
if (StringUtils.isEmpty(className)) {
throw new Exception("类名不能为空!");
}
//替换路劲格式
className = className.replaceAll("\.", "/");
//获取文件信息
FileInputStream fis = new FileInputStream(classPath "/" className ".class");
//获取长度
int len = fis.available();
//文件信息
byte[] data = new byte[len];
//读写文件信息
fis.read(data);
fis.close();
return data;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByClassName(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
/**
* 重写类加载方法,在这里打破双亲委派
* @param name
* @param resolve
* @return
* @throws ClassNotFoundException
*/
@Override
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) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//这里重写逻辑 如果不是自定义类开头就用默认类加载器,如果是就有自定义类加载器加载
if(!name.startsWith("com.classloader")){
c = this.getParent().loadClass(name);
}else{
c = findClass(name);
}
// this is the defining class loader; record the stats
// sun.misc.PerfCounter.getParentDelegationTime().addTime(t1);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
// }
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
public void test(){
System.out.println("测试类!");
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//获取项目路劲
File file = new File(EncryptionUtils.class.getResource("/").getPath());
MySelfClassLoader mySelfClaasLoader = new MySelfClassLoader(file.getPath());
Class<?> loadClass = mySelfClaasLoader.loadClass("com.classloader.simulate.SimulateTomcatClassLoader");
//获取实例对象
Object obj = loadClass.newInstance();
//获取方法列表
Method[] declaredMethods = loadClass.getDeclaredMethods();
System.out.println(declaredMethods);
//获取方法
Method method = loadClass.getMethod("test", null);
method.invoke(obj,null);
System.out.println(loadClass.getClassLoader().getClass().getName());;
}
}
结果
代码语言:javascript复制测试类!
com.classloader.simulate.SimulateTomcatClassLoader$MySelfClassLoader
最后
很多容器都是打破Jvm的双亲委派机制来实现的,比如tomcat,如果不打破的话根据无法部署多个项目,所以打破双亲委派机制也是一种业务场景的需要。而通过了解和实现打破双亲委派机制来学习tomcat的实现,有效提升对jvm底层的实现了解。
参考文章:
https://www.jianshu.com/p/7706a42ba200
https://cwiki.apache.org/confluence/display/tomcat/FAQ
https://blog.csdn.net/lisheng5218/article/details/111475536