目前微服务多采用分层模式,其中基础设施建设尤为重要,相当于系统或平台的根基,比较庞大,但随之而来的问题也逐渐暴露,比如依赖包版本冲突问题,本次介绍的类隔离技术就可以解决这个问题。
类隔离是一种通过类加载器实现加载所需类的实现方式,使得不同版本类间隔离,避免了加载冲突问题。
<!-- common-message-->
<dependency>
<groupId>com.lgy</groupId>
<artifactId>spring-common-message</artifactId>
<version>1.0.0<version>
</dependency>
服务B pom.xml:
<!-- common-message-->
<dependency>
<groupId>com.lgy</groupId>
<artifactId>spring-common-message</artifactId>
<version>2.0.0<version>
</dependency>
// 业务A调用微信服务通知
MessageUtil.sendMessage(content,peopleId,templateId,"wechat");
// 业务B调用微信服务通知
MessageUtil.sendToWechat(content,peopleId,templateId);
涉及的知识点
- JVM加载过程:加载-》链接-》初始化(具体后续介绍)
- 双亲委派机制:委托父加载器查询;如果父加载器查询不到,则调用自身的findClass加载
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class CustomerFindClass extends ClassLoader {
private Map<String, String> classPathMap = new HashMap<>();
public CustomerFindClass() {
// 业务A的自定义类加载器
classPathMap.put("com.lgy.businessA.service.impl.MessageServiceImpl", "E:/dataway-demo/example/target/classes/com/lgy/businessA/service/impl/MessageServiceImpl.class");
classPathMap.put("com.lgy.v1.message.util.MessageUtil", "E:/dataway-demo/example/target/classes/com/lgy/v1/message/util/MessageUtil.class");
}
/**
* findClass方式加载类
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classPath = classPathMap.get(name);
File file = new File(classPath);
if (!file.exists()) {
throw new ClassNotFoundException();
}
byte[] bytes = getClassData(file);
if (null == bytes || 0 == bytes.length) {
throw new ClassNotFoundException();
}
return defineClass(bytes, 0, bytes.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[]{};
}
private ClassLoader classLoader;
/**
* 重新loadClass方法
*/
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class result = null;
try {
//这里要使用 JDK 的类加载器加载 java.lang 包里面的类
result = classLoader.loadClass(name);
} catch (Exception e) {
// ignore error
}
if (null != result) {
return result;
}
String classPath = classPathMap.get(name);
File file = new File(classPath);
if (!file.exists()) {
throw new ClassNotFoundException();
}
byte[] bytes = getClassData(file);
if (null == bytes || 0 == bytes.length) {
throw new ClassNotFoundException();
}
return defineClass(bytes, 0, bytes.length);
}
本文分享的方式是从类加载器方向出发,实现最终的类隔离,避免了不同模块间不同类的冲突,其中顺便也简单带过了jvm类加载相关的知识点,也算是一劳多得,后续会继续跟进。
当然也有很多其它方式可以实现,有时间可以探讨交流。
参考链接: 肖汉松-如何实现Java类隔离加载?