OptaPlanner 在 Spring 启动时出现 java 反射错误

OptaPlanner got java reflection error in Spring boot

我刚刚尝试 运行 在 Spring 引导中创建一个 OptaPlanner 项目,但是 Spring 的 OptaPlanner 用户指南中只有非常简单的文字。

其实,我认为很容易将OptaPlanner项目中的所有域对象、配置文件和drools文件不作任何更改地复制到Spring Boot项目,但唯一的问题是如何调用Solversolve方法。

我在 Spring 启动后使用实现 CommandLineRunner 接口的 class(名为 CommandLineAppStartupRunner)实现了 运行,我调用了 solve 方法在其 run 方法中。最后,我得到如下异常:

Caused by: java.lang.IllegalArgumentException: Can not set org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore field springbootcloudbalance.domain.CloudBalance.score to springbootcloudbalance.domain.CloudBalance
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
at java.lang.reflect.Field.get(Field.java:393)
at org.optaplanner.core.impl.domain.common.accessor.ReflectionFieldMemberAccessor.executeGetter(ReflectionFieldMemberAccessor.java:54)
at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.getScore(SolutionDescriptor.java:1071)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.cloneSolution(AbstractScoreDirector.java:212)
at org.optaplanner.core.impl.solver.scope.DefaultSolverScope.setWorkingSolutionFromBestSolution(DefaultSolverScope.java:230)
at org.optaplanner.core.impl.solver.AbstractSolver.solvingStarted(AbstractSolver.java:75)
at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:210)
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:190)
at springbootcloudbalance.CommandLineAppStartupRunner.run(CommandLineAppStartupRunner.java:55)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:818)
... 10 common frames omitted

我检查了代码,发现异常抛出是因为来自 field.getDeclaringClass() 的对象与来自 var1.getClass() 的对象是不同的实例。恐怕是由于 java OptaPlanner 和 Spring Boot.

反射冲突的实现

我使用的版本如下:

删除 spring-boot-devtools 依赖修复了这个错误。另一个与此类似的 SO 问题解释说它与不同的类加载器有关:。接受的答案还提到了一个可能的修复:

To fix it, configure spring dev tools to load Drools libraries in the RestartClassLoader, together with the project's classes: using-boot-devtools-customizing-classload

尼克的回答是正确的。这只是为了弄清楚发生了什么。

这一行表示optaplanner正在提取CloudBalance.getScore():

at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.getScore(SolutionDescriptor.java:1071)

这一行意味着它为此使用了 ReflectionFieldMemberAccessor,这只是通过反射读取字段(包括私有字段)的方法:

at org.optaplanner.core.impl.domain.common.accessor.ReflectionFieldMemberAccessor.executeGetter(ReflectionFieldMemberAccessor.java:54)

现在错误消息变得有趣了:

Can not set ...HardMediumSoftScore field ...CloudBalance.score to ...CloudBalance

看起来基本上是这样的:

CloudBalance cloudBalance2 = cloudBalance.getScore();

嗯?

Musa 提供了这个答案,但有人删除了它,尽管 JIRA link 非常相关,因为它指出哪个版本的 OptaPlanner 可以更好地处理这个问题:

"An issue was submitted to OptaPlanner to provide better error messages for such cases: https://issues.jboss.org/browse/PLANNER-1586. Feel free to add any comments or suggestions."