背景:

在springboot项目中引用drools进行规则推理时,明明用junit测试drools的规则可以正常运行,但是当从项目中启动时候发现规则无法正常运行,凡是涉及fact对象的所有都失效,如果将规则的条件改为eval(true)则可以正常运行。

解决:

初步猜测是drools的工作内存出现的问题(working memory),因为drools的insert fact会将对象放置在working memory中,猜测是不是dubbo线程引起的,所以将推理那块改为多线程的方式,发现问题同样存在。

最后在一个类似的帖子中发现类似的相关问题,所以尝试下发现果然可以。就是springboot引入了热部署工具spring-boot-devtools导致的锅。

springboot 自定义类加载器加载jar包 springboot类加载器冲突_加载


在帖子中没有进行很详细的说明,只是大体说了下类加载器的问题,确实是因为类加载器的问题,在drools的官方网站中有人提出了这个问题,并认为是个bug,但是drools的开发者认为这不是一个bug,地址:https://issues.jboss.org/browse/DROOLS-1540

springboot 自定义类加载器加载jar包 springboot类加载器冲突_类加载器_02


说白了就是drools用的是lancher classloader,而devtools会把我们自己编写的model认为是会随时更改的类,所以采用的是Restart ClassLoader类加载器进行加载(为了快速进行热部署),devtools会有两个类加载器,一个Classloader加载那些不会改变的类(第三方Jar包),另一个ClassLoader加载会更改的类,称为 Restart ClassLoader。

当采用Launcher ClassLoader加载的A 类与Restart Class Loader的A类进行对比时,发现不一致,所以drools引擎自然无法进行识别(这点与大哥@干净的句号说的一致,大哥就是大哥,吃的盐果然不少)。springboot 项目启动(采用devtools工具):

springboot 自定义类加载器加载jar包 springboot类加载器冲突_类加载器_03


drools采用的类加载器是AppClassLoader,而insert的fact对象采用的类加载器为:

springboot 自定义类加载器加载jar包 springboot类加载器冲突_spring_04


可以看到采用的是RestartClassLoader,因为类加载器不一致,并不是由双亲委派模型的下的上级类加载器加载进来,对于一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同Class实例(所有通过正常双亲委派模式的类加载器加载的classpath下的和ext下的所有类在方法区都是同一个类,堆中的Class实例也是同一个)。因为两者不一致,所以drools在workingmemory中使用的是AppAppClassLoader的实例自然无法找到由RestartClassLoader加载的实例。多说一点,RestartClassLoader打破了双亲委派模型,其源码:

springboot 自定义类加载器加载jar包 springboot类加载器冲突_加载_05


所以为了满足热更新的需求,不需要父加载器重新生成加载,只需要自身去查找加载类即可,这也是其能热更新的主要原因。在Junit测试下,两者采用的都是AppClassLoader:

springboot 自定义类加载器加载jar包 springboot类加载器冲突_类加载器_06

所以解决办法就是将devtools的maven依赖去掉即可,或者采用drools官网中说明的其它方法,因为太多英文没耐心看下去。