上一篇 <<<ClassLoader类加载器顺序Demo测试与双亲委派源码解读
下一篇 >>>JVM中对象如何完成空间分配和初始化工作
1.SPI破解类加载器的双亲委派模式
Java SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制.
a、项目结构图
b、演示效果
//1.不设置,或设置当前线程的类加载器为APP加载器,则load正常读取
// Thread.currentThread().setContextClassLoader(SPITest.class.getClassLoader());
//2.设置当前线程的加载类为扩展加载,则load读取不到,因为扫不到包
// Thread.currentThread().setContextClassLoader(SPITest.class.getClassLoader().getParent());
//3.设置当前线程的加载类为启动类,则load可正常读取
// Thread.currentThread().setContextClassLoader(SPITest.class.getClassLoader().getParent().getParent());
//
ServiceLoader<JaryeService> load = ServiceLoader.load(JaryeService.class);
Iterator<JaryeService> iterator = load.iterator();
while (iterator.hasNext()) {
JaryeService next = iterator.next();
System.out.println(next + "\t" + next.getName());
}
c、源码解析
从源码中可以看到使用此方式不存在先执行parent的情况,即可破解了双亲委派模式。
数据库连接方式的底层实现也是此方式:
Connection root = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8","root", "root");
DriverManager的初始化方法:loadInitialDrivers()
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
2.自定义类加载器的方式破坏双亲委派模式(热部署)
a、继承ClassLoader
b、重写findClass方法,其中调用父类的defineClass方法
public class JaryeClassLoader extends ClassLoader {
/**
* 重写findClass方法
* @param name
* @return
*/
@Override
public Class<?> findClass(String name) {
try {
byte[] data = getClassFileBytes(this.fileObject);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class ManualHotDeployment {
private static long startTime;
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
// 通过自定义类加载器,读取信息
JaryeClassLoader jaryeClassLoader = new JaryeClassLoader();
jaryeClassLoader.setFileObject(new File("/Users/jiangjinrong/Downloads/OrderServiceImpl.class"));
Class<?> aClass = jaryeClassLoader.findClass("com.jarye.classloader.service.OrderServiceImpl");
OrderService orderService = (OrderService) aClass.newInstance();
System.out.println("读取class成功:" + orderService.addOrder());
File fileObject = jaryeClassLoader.getFileObject();
startTime = fileObject.lastModified();
// 定时轮询读取信息并通过类加载器更新
new Thread(() -> {
while (true) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
long endTime = fileObject.lastModified();
if (endTime != startTime) {
System.out.println("class文件发生了变化,需要重新读取");
try {
JaryeClassLoader newMayiktClassLoader = new JaryeClassLoader();
newMayiktClassLoader.setFileObject(new File("/Users/jiangjinrong/Downloads/OrderServiceImpl.class"));
Class<?> newClass = newMayiktClassLoader.findClass("com.jarye.classloader.service.OrderServiceImpl");
OrderService newOrderService = (OrderService) newClass.newInstance();
System.out.println("读取class成功:" + newOrderService.addOrder());
} catch (Exception e) {
e.printStackTrace();
}
startTime = endTime;
}
}
}).start();
}
}
热部署与热加载的区别
热部署的案例:SpringBoot、tomcat、jetty
相同点:
a.不重启服务器编译/部署项目
b.基于Java的类加载器实现
不同点:
a、部署方式
热部署在服务器运行时重新部署项目
热加载在运行时重新加载class
b、实现原理
热部署直接重新加载整个应用
热加载在运行时重新加载class
c、使用场景
热部署更多的是在生产环境使用
热加载则更多的实在开发环境使用
相关文章链接:
<<<JVM整体内存结构的图解,直观明了
<<<javap命令查看对象信息及操作方法在JVM层的实现原理
<<<javap命令反查汇编指令汇总
<<<ClassLoader类加载器顺序Demo测试与双亲委派源码解读
<<<JVM中对象如何完成空间分配和初始化工作
<<<JVM元空间(方法区)和栈内存溢出原因及解决方案
<<<JVM堆内存溢出和内存泄露问题定位和解决
<<<JVM常见死锁问题产生原因和多种诊断方式
<<<服务器CPU飙升为100%问题排查及如何避免
<<<JVM内存诊断命令和排查工具汇总
<<<JVM新生代老年代算法汇总图解
<<<JVM垃圾回收不要手动System.gc的真正原因
<<<JVM垃圾回收引用计数法和根搜索算法图解
<<<JVM垃圾回收STW(Stop-The-World)代码演示
<<<JVM垃圾回收器的发展历程及使用场景汇总
<<<JVM串行并行垃圾回收器的关注点
<<<一张图看懂CMS垃圾回收器的底层原理
<<<G1能作为JDK9默认垃圾回收器的优势分析
<<<CMS和G1的漏标问题解决及三色标记算法图解
<<<GC中新生代进入老年代的方式汇总
<<<GC常用日志参数配置及分析工具说明
<<<FullGC、MinorGC、STW等常见问题如何解答
<<<JVM性能调优的评估指标及调优示例
网友评论