今天周末在家,看看电影,写写代码
来实践一把自定义类加载器、打破双亲委派机制,记录下踩坑
自定义类加载器
步骤:
- 继承ClassLoader
- 重写findClass方法
自定义MyClassLoader
import java.io.FileInputStream;
import java.io.IOException;
/**
* Created by IntelliJ IDEA
* User: yqm02
* Date: 2021/8/22
* Time: 16:14
*/
public class MyClassLoader extends ClassLoader{
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
/**
* 到指定目录下面去加载类
* @param name 指定目录下的类名称
* 权限修饰符:private 当前类可访问 default 同一包下可访问 protected 同一包下、不同包子类可访问 public 不同包非子类也可以访问
*
*/
@Override
protected Class<?> findClass(String name) {
byte[] data = new byte[0];
String nameTemp = name;
try {
// 从磁盘读取一个.class文件
name = name.replaceAll("\\.", "/");
String url = classPath + "/" + name + ".class";
FileInputStream fileInputStream = new FileInputStream(url);
int len = fileInputStream.available();
data = new byte[len];
fileInputStream.read(data);
fileInputStream.close();
} catch (IOException e) {
// 日志记录,io异常
}
// 将一个字节数组转为class对象
return defineClass(nameTemp, data, 0, data.length);
}
}
测试
import cn.onedawn.classloader.MyClassLoader;
import java.lang.reflect.InvocationTargetException;
/**
* Created by IntelliJ IDEA
* User: yqm02
* Date: 2021/8/22
* Time: 16:13
*/
public class MyClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 初始化MyClassLoader
MyClassLoader classLoader = new MyClassLoader("D:");
// 指定加载哪一个类
Class clazz = classLoader.loadClass("test.User");
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
打破双亲委派机制
步骤:
- 继承ClassLoader
- 重写findClass方法
- 重写loadClass方法
自定义MyClassLoaderNoParent
import sun.misc.PerfCounter;
import java.io.FileInputStream;
import java.io.IOException;
/**
* Created by IntelliJ IDEA
* User: yqm02
* Date: 2021/8/22
* Time: 17:25
*/
public class MyClassLoaderNoParent extends ClassLoader {
/**
* 加载器加载的路径
* */
private String classPath;
public MyClassLoaderNoParent(String path) {
this.classPath = path;
}
protected byte[] loadByte(String name) throws IOException {
name = name.replaceAll("\\.", "/");
String path = classPath + "/" + name + ".class";
FileInputStream fileInputStream = new FileInputStream(path);
int len = fileInputStream.available();
byte[] data = new byte[len];
fileInputStream.read(data);
fileInputStream.close();
return data;
}
@Override
protected Class<?> findClass(String name) {
byte[] data = null;
try {
data = loadByte(name);
} catch (IOException e) {
e.printStackTrace();
}
return defineClass(name, data, 0, data.length);
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
// 保证Object等必须要加载的类从启动类加载
if (!name.startsWith("cn.onedawn.User")) {
c = this.getParent().loadClass(name);
} else {
c = findClass(name);
}
long t1 = System.nanoTime();
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
测试
import cn.onedawn.classloader.MyClassLoaderNoParent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by IntelliJ IDEA
*
* @Description: TODO
* @author: yqm02
* @Date: 2021/8/22
*/
public class MyClassLoaderNoParentTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
MyClassLoaderNoParent myClassLoaderNoParent = new MyClassLoaderNoParent("D:/test");
// 确定加载哪一个类
Class clazz = myClassLoaderNoParent.loadClass("cn.onedawn.User");
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
踩坑记录
今天尝试去指定目录下加载java.lang.String
,但是无论如何都不能成功,沙箱安全机制
记住,像Object,String这样最基础的类必须是由BootStrapClassLoader来加载
另外再贴一个为什么要有双亲委派机制?
- 沙箱安全机制
- 避免类的重复加载