我是靠谱客的博主 清秀店员,最近开发中收集的这篇文章主要介绍类加载器之-----使用spring-boot-devtools与drools导致的 com.x.y.A cannot be cast to com.x.y.A问题由来问题分析:解决办法:,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

我们知道判断两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义。使用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问题由来问题分析:解决办法:所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部