概述
我们知道判断两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义。使用spring-boot-devtools时,我们很多“”应用类“”是由spring提供的org.springframework.boot.devtools.restart.classloader.RestartClassLoader加载,而不是以前的sun.misc.Launcher$AppClassLoader。因此肯能出现, 虽然对象的类名一样,但是却无法直接赋值或者引用。
注意:关于类加载机制,请百度或者参考《深入理解Java虚拟机》一书
问题由来
springboot程序中为了调试方便添加了热加载依赖包spring-boot-devtools。 程序中线程池进行drools规则运算并将结果返回,结果返回的结果不能直接直接使用。 例如线程池返回的是class A的对象a1, 当你直接在代码中直接对返回对象 操作时,报ClassCastException, 例如当执行A a2 = a1., 无法之下只能执行属性copy.
Caused by: java.lang.ClassCastException: com.yq.domain.User cannot be cast to com.yq.domain.User
... 11 common frames omitted
A a2 = new A()
BeanUtils.copyProperties(a1, a2);
问题分析:
两个都是class A但是却无法执行A a2 = a1.操作,根据类加载机制,初步判断两个类加载器不一样,加上日志发现:
完整的代码在这里,欢迎加星, fork, 谢谢!
具体代码(Runnable中打印)
@Override
public void run() {
long threadId = Thread.currentThread().getId();
userId = (String) dataMap.get("userId");
long begin = System.currentTimeMillis();
log.debug("start for userId={}, threadId={}", userId, threadId);
DroolsProcessor processor = new DroolsProcessor();
List<User> list = new ArrayList<>();
String ruleContent = "this["S1"]>10";
String head = DroolsUtils.makeRuleHead();
String body = DroolsUtils.makeRuleBody(ruleContent);
String fullDroolRule = head + body;
try {
processor.initProcessor(userId, fullDroolRule);
dataMap.put("S1", 20);
processor.process(dataMap, list);
} catch (Exception ex) {
log.error("exception. userId={}, threadId={}", userId, threadId, ex);
return;
}
//drools运行产生一批User对象
for (Object obj : list) {
User u2 = new User();
BeanUtils.copyProperties(obj, u2);
log.info("obj={}", obj);
try {
ClassLoader classLoader = obj.getClass().getClassLoader();
String objClassName = obj.getClass().getCanonicalName();
if (obj instanceof User) {
log.info("drools uses loader={}, objClassName={}, threadId={}", classLoader, objClassName, threadId);
} else {
ClassLoader userClassLoader = User.class.getClassLoader();
log.info("objLoader={}, userLoader={}, objClassName={},threadId={}", classLoader, userClassLoader, objClassName, threadId);
}
} catch (Exception ex) {
log.debug("not the same class", ex);
}
}
long end = System.currentTimeMillis();
log.debug("end for userId={}, cost={}, threadId={}", userId, end - begin, threadId);
}
drools的bug在
https://issues.jboss.org/browse/DROOLS-1540
我现在使用的6.5.0.Final, 官方在19年5月23提交fix, 在7.23.0.Final版本包含了该fix。但是6升级到7有很多改动,原有drools代码不能直接使用
解决办法:
根据分析结果, 我们的办法有一下几种:
第一种,不使用spring-boot-devtools, 这样class A的类加载器是一样。
第二种,让线程使用RestartClassLoader
第三种,就是拷贝属性
第四种,升级drools版本(需要修改自己的drools相关代码)
第一,和第三种试验可以,但是第二种,虽然可以通过setContextClassLoader方法修改线程的累加器,但是实际测试发现,线程中的class A始终是由AppClassLoader加载的。也就是第二种目前行不通。
欢迎大家查看我的源码,如果那些设置setContextClassLoader不对,欢迎指正,谢谢!
最后
以上就是清秀店员为你收集整理的类加载器之-----使用spring-boot-devtools与drools导致的 com.x.y.A cannot be cast to com.x.y.A问题由来问题分析:解决办法:的全部内容,希望文章能够帮你解决类加载器之-----使用spring-boot-devtools与drools导致的 com.x.y.A cannot be cast to com.x.y.A问题由来问题分析:解决办法:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复