java classLoad 类隔离
1. 不同类:JVM 中一个类的唯一标识是 类加载器+类名
, 这里类加载器指的是类加载器的实例,并不是一定要定义两个不同类加载器
2. 类加载传导规则:JVM 会选择当前类的类加载器来加载所有该类的引用类
package cn.jiayeli.riverMpas.tests;
import org.junit.Test;
/**
* @author: jiayeli.cn
* @description multi version jas supported class load
* @date: 2023/8/6 下午2:03
*/
public class ClassLoadTestCase {
public void classLoadTestCase() {
TestA testA = new TestA();
TestB testb = new TestB();
testA.hello();
testb.hello();
}
}
class TestA {
public void hello() {
System.out.println("TestA: " + this.getClass().getClassLoader());
}
}
class TestB {
public void hello() {
System.out.println("TestB: " + this.getClass().getClassLoader());
}
}
TestA: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
使用双亲委派机制进行加载,两个类用为同一classLoad加载
ClassLoader.findClass
来自定义内加载器package cn.jiayeli.riverMpas.tests;
import org.junit.Test;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* @author: jiayeli.cn
* @description multi version jars supported class load
* @date: 2023/8/6 下午2:03
*/
public class ClassLoadTestCase {
@Test
public void partentFireTestCase() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
ParentFireClassLoadTestCase parentFistClassLoad = new ParentFireClassLoadTestCase();
Class testAClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestA");
Class testBClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestB");
Method aHello = testAClass.getDeclaredMethod("hello", String.class);
Method helloB = testBClass.getDeclaredMethod("hello");
aHello.invoke(testAClass.getConstructor().newInstance(), "args");
helloB.setAccessible(true);
helloB.invoke(testBClass.newInstance(), null);
}
}
class ParentFireClassLoadTestCase extends ClassLoader{
private Map<String, String> classPathMap = new HashMap<>();
public ParentFireClassLoadTestCase() {
classPathMap.put("cn.jiayeli.riverMpas.tests.TestA", "target/test-classes/cn/jiayeli/riverMpas/tests/TestA.class");
classPathMap.put("cn.jiayeli.riverMpas.tests.TestB", "target/test-classes/cn/jiayeli/riverMpas/tests/TestB.class");
}
//在此类装入器的类路径上搜索并装入类, ru guo zhaobudao jiu diaoyong fulei jiazaiqijinxin jiazai
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
String classPath = classPathMap.get(name);
Function<String, Class<?>> classFind = classFileName -> {
File classFile = new File(classFileName);
Class<?> clazz = null;
if (classFile.exists()) {
try {
byte[] classBytes = Optional.ofNullable(getClassData.apply(classFile))
.orElseThrow(() -> new ClassNotFoundException());
clazz = defineClass(name, classBytes, 0, classBytes.length);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return clazz;
};
return classFind.apply(classPath);
}
private Function<File, byte[]> getClassData = (File classFile) -> {
byte[] bytes = null;
try(InputStream ins = new FileInputStream(classFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream()
) {
byte[] buffer = new byte[4096];
int read;
while ((read = ins.read(buffer)) != -1) {
baos.write(buffer, 0, read);
}
bytes = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return bytes == null ? new byte[]{} : bytes;
};
}
TestA: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@1e88b3c
TestC: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@1e88b3c
TestA中依赖C,但是由于双亲委派的影响,A使用用户自定义类加载器加载,而C则由其父加载器进行了加载。
package cn.jiayeli.riverMpas.tests;
/**
* @author: jiayeli.cn
* @description
* @date: 2023/8/6 下午3:53
*/
public class TestA {
public void hello() {
System.out.println("TestA: " + this.getClass().getClassLoader());
}
public void hello(String arg1) {
System.out.println("TestA: " + this.getClass().getClassLoader());
TestC testC = new TestC();
testC.hello();
}
}
package cn.jiayeli.riverMpas.tests;
/**
* @author: jiayeli.cn
* @description
* @date: 2023/8/6 下午4:02
*/
public class TestB {
public void hello() {
System.out.println("TestB: " + this.getClass().getClassLoader());
}
}
package cn.jiayeli.riverMpas.tests;
/**
* @author: jiayeli.cn
* @description
* @date: 2023/8/6 下午4:07
*/
public class TestC {
public void hello() {
System.out.println("TestC: " + this.getClass().getClassLoader());
}
}
ClassLoader.loadClass
方法打破双亲委派package cn.jiayeli.riverMpas.tests;
import org.junit.Test;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
/**
* @author: jiayeli.cn
* @description multi version jas supported class load
* @date: 2023/8/6 下午2:03
*/
public class ClassLoadTestCase {
@Test
public void classLoadTestCase() {
TestA testA = new TestA();
TestB testb = new TestB();
testA.hello();
testb.hello();
System.out.println("----------------------- default jdk classloader test -------------------------------\n");
}
@Test
public void parentFistClassLoaderTestCase() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
ParentFireClassLoadTestCase parentFistClassLoad = new ParentFireClassLoadTestCase();
Class testAClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestA");
Class testBClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestB");
Method aHello = testAClass.getDeclaredMethod("hello", String.class);
Method helloB = testBClass.getDeclaredMethod("hello");
aHello.invoke(testAClass.getConstructor().newInstance(), "args");
helloB.setAccessible(true);
helloB.invoke(testBClass.newInstance(), null);
System.out.println("----------------------- overwrite findClass function classloader test -------------------------------\n");
}
@Test
public void customerClassLoaderTestCase() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
ClassLoader parentClassLoad = Thread.currentThread().getContextClassLoader().getParent();
CustomerClassLoader customerClassLoader = new CustomerClassLoader(parentClassLoad);
Class testAClass = customerClassLoader.loadClass("cn.jiayeli.riverMpas.tests.TestA", false);
Class testBClass = customerClassLoader.loadClass("cn.jiayeli.riverMpas.tests.TestB", false);
Method helloA = testAClass.getDeclaredMethod("hello", String.class);
Method helloB = testBClass.getDeclaredMethod("hello");
helloA.invoke(testAClass.getConstructor().newInstance(), "args");
helloB.invoke(testBClass.getConstructor().newInstance(), null);
System.out.println("----------------------- overwrite loadClass function classloader test -------------------------------\n");
}
}
class ParentFireClassLoadTestCase extends ClassLoader{
private Map<String, String> classPathMap = new HashMap<>();
public ParentFireClassLoadTestCase() {
classPathMap.put("cn.jiayeli.riverMpas.tests.TestA", "target/test-classes/cn/jiayeli/riverMpas/tests/TestA.class");
classPathMap.put("cn.jiayeli.riverMpas.tests.TestB", "target/test-classes/cn/jiayeli/riverMpas/tests/TestB.class");
}
//在此类装入器的类路径上搜索并装入类
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
String classPath = classPathMap.get(name);
Function<String, Class<?>> classFind = classFileName -> {
File classFile = new File(classFileName);
Class<?> clazz = null;
if (classFile.exists()) {
try {
byte[] classBytes = Optional.ofNullable(getClassData.apply(classFile))
.orElseThrow(() -> new ClassNotFoundException());
clazz = defineClass(name, classBytes, 0, classBytes.length);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return clazz;
};
return classFind.apply(classPath);
}
public static Function<File, byte[]> getClassData = (File classFile) -> {
byte[] bytes = null;
try(InputStream ins = new FileInputStream(classFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream()
) {
byte[] buffer = new byte[4096];
int read;
while ((read = ins.read(buffer)) != -1) {
baos.write(buffer, 0, read);
}
bytes = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return bytes == null ? new byte[]{} : bytes;
};
}
class CustomerClassLoader extends ClassLoader{
private ClassLoader jdkClassLoader;
private Map<String, String> classPathMap = new HashMap<>();
public CustomerClassLoader(ClassLoader jdkClassLoader) {
this.jdkClassLoader = jdkClassLoader;
classPathMap.put("cn.jiayeli.riverMpas.tests.TestA", "target/test-classes/cn/jiayeli/riverMpas/tests/TestA.class");
classPathMap.put("cn.jiayeli.riverMpas.tests.TestB", "target/test-classes/cn/jiayeli/riverMpas/tests/TestB.class");
// ClassA.hello("")方法需要ClassC,
classPathMap.put("cn.jiayeli.riverMpas.tests.TestC", "target/test-classes/cn/jiayeli/riverMpas/tests/TestC.class");
}
//
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
AtomicReference<Exception> exception = new AtomicReference<>();
Class<?> clazz = null;
/*jdk 包里面的类使用JDK的类加载器加载*/
try {
clazz = jdkClassLoader.loadClass(name);
} catch (ClassNotFoundException e) {
System.out.println("jdk classLoad cannot loading class: " + name);
}
clazz = Optional.ofNullable(clazz)
.orElseGet(() -> {
File classFile = new File(classPathMap.get(name));
Class findClazz = null;
if (classFile.exists()) {
try {
byte[] classBytes = Optional.ofNullable(ParentFireClassLoadTestCase.getClassData.apply(classFile))
.orElseThrow(() -> new ClassNotFoundException());
findClazz = defineClass(name, classBytes, 0, classBytes.length);
} catch (ClassNotFoundException e) {
e.printStackTrace();
exception.set(e);
}
}
return findClazz;
});
return Optional.ofNullable(clazz).orElseThrow(() -> (ClassNotFoundException) exception.get());
}
}
TestA: cn.jiayeli.riverMpas.tests.CustomerClassLoader@5e5792a0
TestC: cn.jiayeli.riverMpas.tests.CustomerClassLoader@5e5792a0
TestB: cn.jiayeli.riverMpas.tests.CustomerClassLoader@5e5792a0
----------------------- overwrite loadClass function classloader test -------------------------------
TestA: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
----------------------- default jdk classloader test -------------------------------
TestA: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@754ba872
TestC: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@754ba872
----------------------- overwrite findClass function classloader test -------------------------------
重写loadClass方法的自定义类加载器进行类加载时目标加载类及其依赖的类都会由自定义加载器进行加载。