阿里妹导读:Java 开发中,如果不同的 jar 包依赖了某些通用 jar 包的版本不一样,运行时就会因为加载的类跟预期不符合导致报错。如何避免这种情况呢?本文通过分析 jar 包产生冲突的原因及类隔离的实现原理,分享两种实现自定义类加载器的方法。
文末福利:《全面讲解Spring Cloud Alibaba技术栈》技术公开课。
public class TestA {
public static void main(String[] args) {
TestA testA = new TestA();
testA.hello();
}
public void hello() {
System.out.println("TestA: " + this.getClass().getClassLoader());
TestB testB = new TestB();
testB.hello();
}
}
public class TestB {
public void hello() {
System.out.println("TestB: " + this.getClass().getClassLoader());
}
}
public class MyClassLoaderParentFirst extends ClassLoader{
private Map<String, String> classPathMap = new HashMap<>();
public MyClassLoaderParentFirst() {
classPathMap.put("com.java.loader.TestA", "/Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestA.class");
classPathMap.put("com.java.loader.TestB", "/Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestB.class");
}
// 重写了 findClass 方法
public Class<?> findClass(String name) throws ClassNotFoundException {
String classPath = classPathMap.get(name);
File file = new File(classPath);
if (!file.exists()) {
throw new ClassNotFoundException();
}
byte[] classBytes = getClassData(file);
if (classBytes == null || classBytes.length == 0) {
throw new ClassNotFoundException();
}
return defineClass(classBytes, 0, classBytes.length);
}
private byte[] getClassData(File file) {
try (InputStream ins = new FileInputStream(file); ByteArrayOutputStream baos = new
ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return new byte[] {};
}
}
public class MyTest {
public static void main(String[] args) throws Exception {
MyClassLoaderParentFirst myClassLoaderParentFirst = new MyClassLoaderParentFirst();
Class testAClass = myClassLoaderParentFirst.findClass("com.java.loader.TestA");
Method mainMethod = testAClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke(null, new Object[]{args});
}
TestA: com.java.loader.MyClassLoaderParentFirst@1d44bcfa
TestB: sun.misc.Launcher$AppClassLoader@18b4aac2
委托给父加载器查询
如果父加载器查询不到,就调用 findClass 方法进行加载
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
public class MyClassLoaderCustom extends ClassLoader {
private ClassLoader jdkClassLoader;
private Map<String, String> classPathMap = new HashMap<>();
public MyClassLoaderCustom(ClassLoader jdkClassLoader) {
this.jdkClassLoader = jdkClassLoader;
classPathMap.put("com.java.loader.TestA", "/Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestA.class");
classPathMap.put("com.java.loader.TestB", "/Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestB.class");
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class result = null;
try {
//这里要使用 JDK 的类加载器加载 java.lang 包里面的类
result = jdkClassLoader.loadClass(name);
} catch (Exception e) {
//忽略
}
if (result != null) {
return result;
}
String classPath = classPathMap.get(name);
File file = new File(classPath);
if (!file.exists()) {
throw new ClassNotFoundException();
}
byte[] classBytes = getClassData(file);
if (classBytes == null || classBytes.length == 0) {
throw new ClassNotFoundException();
}
return defineClass(classBytes, 0, classBytes.length);
}
private byte[] getClassData(File file) { //省略 }
}
public class MyTest {
public static void main(String[] args) throws Exception {
//这里取AppClassLoader的父加载器也就是ExtClassLoader作为MyClassLoaderCustom的jdkClassLoader
MyClassLoaderCustom myClassLoaderCustom = new MyClassLoaderCustom(Thread.currentThread().getContextClassLoader().getParent());
Class testAClass = myClassLoaderCustom.loadClass("com.java.loader.TestA");
Method mainMethod = testAClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke(null, new Object[]{args});
}
}
TestA: com.java.loader.MyClassLoaderCustom@1d44bcfa
TestB: com.java.loader.MyClassLoaderCustom@1d44bcfa
参考资料 深入探讨 Java 类加载器 (https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html)
本课程共10个课时,课程内容包括:系统架构的演变过程、微服务架构的问题及解决思路、Spring Cloud Alibaba的功能和组件介绍、实例项目的逻辑思路、微服务的环境搭建,并通过实战案例加深对Spring Cloud Aliaba技术栈中的技术点的学习与理解。
点击“阅读原文”,快去学习吧~