前言:关于contextClassLoader的使用场景
基础知识:
类加载机制,双亲委派模式,SPI机制。这里不赘述,不懂的小伙伴们自己补补基础吧~
JDK SPI机制
关于SPI机制可以看下这篇SPI机制
重点关注JDK的SPI机制
其中有提到ServiceLoader#load的源码
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
实践
1.目录结构
如图,service依赖dao,红线框出来的是用到的类。
相关的代码
1.dao中
public interface ColourConfig {
String myColour();
}
以下classLoader是抄的网上某大神的代码,出处找不到了,主要是研究contextClassLoader,所以这里的代码没有仔细打磨。
public class MyClassLoader extends ClassLoader {
private String path ;
public MyClassLoader(String classPath) {
path = classPath;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
Class log = null;
// 获取该class文件字节码数组
byte[] classData = getData();
if (classData != null) {
// 将class的字节码数组转换成Class类的实例
log = defineClass(name, classData, 0, classData.length);
}
return log;
}
private byte[] getData() {
File file = new File(path);
if (file.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int size = 0;
while ((size = in.read(buffer)) != -1) {
out.write(buffer, 0, size);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return out.toByteArray();
} else {
return null;
}
}
}
public class JavaColorColourFactory {
public static ColourConfig getColor(){
ServiceLoader<ColourConfig> colourLoader = ServiceLoader.load(ColourConfig.class);
Iterator<ColourConfig> colourIterator = colourLoader.iterator();
ColourConfig colourConfig = null;
while (colourIterator.hasNext()){
colourConfig = colourIterator.next();
}
return colourConfig;
}
}
配置文件:
com.dongdong.myheart.service.spi.BlueConfig
2.service模块
public class BlueConfig implements ColourConfig {
@Override
public String myColour() {
return "blue123";
}
}
3.测试:
@SpringBootTest
class MyheartDaoApplicationTests {
@Test
void testContextLoads() {
try {
//这里class文件写全路径,我没写全
MyClassLoader blueClassLoder = new MyClassLoader("**\\BlueConfig.class");
//看,这里设置了上下文类加载器
Thread.currentThread().setContextClassLoader(blueClassLoder);
ColourConfig colourConfig = JavaColorColourFactory.getColor();
Class<?> blueConfig = colourConfig.getClass();
Method myColour = blueConfig.getMethod("myColour");
Object blueColour = myColour.invoke(colourConfig);
ClassLoader factoryClassLoaderReally = JavaColorColourFactory.class.getClassLoader();
ClassLoader blueClassLoaderReally = colourConfig.getClass().getClassLoader();
ClassLoader blueParent = blueClassLoaderReally.getParent();
ClassLoader currentClassLoader = MyheartDaoApplicationTests.class.getClassLoader();
System.out.println(blueClassLoaderReally);
}catch (Exception e){
e.printStackTrace();
}
}
}
以上class的全路径,我是直接编译后找到的target目录下,右键copy path获得的全路径
4.执行结果
上述可以明显看出,当前测试类以及Factory的类加载器是AppClassLoader,而实现类BlueConfig的类加载器为MyClassLoader,MyClassLoader的parent为AppClassLoader,所以这里是在父类加载器中,加载了子类加载器所加载的类。打破了双亲委派模式。