使用OptaPlanner Spring Boot Starter配置ListChangeMove过滤器
Got it, let's tackle this problem step by step. Since you're using the OptaPlanner Spring Boot Starter and avoiding XML configs, you'll need to define your move selector filters programmatically using either the SolverConfig API or OptaPlanner's annotation-based configuration.
First, a quick note on why your XML config might not have worked: when using the Spring Boot Starter, Java-based configuration often takes priority over XML, or your filter class might have had a typo in its fully qualified name, or wasn't being scanned properly by the Spring context. Let's jump straight to the working programmatic approaches.
1. Configure via SolverConfig @Bean
You can define a SolverConfig bean directly in your Spring configuration, building out the local search phase and move selector with your filter:
import org.optaplanner.core.config.solver.SolverConfig; import org.optaplanner.core.config.phase.LocalSearchPhaseConfig; import org.optaplanner.core.config.heuristic.selector.move.composite.UnionMoveSelectorConfig; import org.optaplanner.core.config.heuristic.selector.move.generic.ListChangeMoveSelectorConfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class OptaPlannerConfig { @Bean public SolverConfig solverConfig() { // Start with a base default config (or build entirely from scratch) SolverConfig solverConfig = SolverConfig.createFromXmlResource("org/optaplanner/core/config/solver/defaultSolverConfig.xml"); // Set your solution and entity classes specific to your vehicle routing problem solverConfig.withSolutionClass(VehicleRoutingSolution.class) .withEntityClasses(Visit.class); // Build custom local search phase LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig(); UnionMoveSelectorConfig unionMoveSelectorConfig = new UnionMoveSelectorConfig(); // Configure ListChangeMoveSelector with your filter ListChangeMoveSelectorConfig listChangeMoveSelectorConfig = new ListChangeMoveSelectorConfig(); // Option 1: Specify the filter class (OptaPlanner will handle instantiation) listChangeMoveSelectorConfig.setFilterClass(MyListChangeMoveFilter.class); // Option 2: Inject a Spring-managed filter bean (useful if your filter needs dependencies) // listChangeMoveSelectorConfig.setFilter(myListChangeMoveFilter()); unionMoveSelectorConfig.addMoveSelectorConfig(listChangeMoveSelectorConfig); localSearchPhaseConfig.setMoveSelectorConfig(unionMoveSelectorConfig); // Replace default phases with your custom local search phase solverConfig.setPhaseConfigs(List.of(localSearchPhaseConfig)); return solverConfig; } // If using Option 2, define your filter as a Spring bean @Bean public MyListChangeMoveFilter myListChangeMoveFilter() { return new MyListChangeMoveFilter(); } }
2. Configure via @PlanningSolver Annotation
If you're using OptaPlanner 8+ annotation-based solver definition, use @PlanningSolverConfigurer to customize the solver config directly in your solver class:
import org.optaplanner.core.api.solver.PlanningSolver; import org.optaplanner.core.api.solver.PlanningSolverConfigurer; import org.optaplanner.core.config.phase.LocalSearchPhaseConfig; import org.optaplanner.core.config.heuristic.selector.move.composite.UnionMoveSelectorConfig; import org.optaplanner.core.config.heuristic.selector.move.generic.ListChangeMoveSelectorConfig; @PlanningSolver public class VehicleRoutingSolver { @PlanningSolverConfigurer public void configure(PlanningSolverConfigurer<VehicleRoutingSolution> configurer) { configurer.getSolverConfig() // Override default phases with your custom local search setup .withPhaseConfigs(List.of(createLocalSearchPhaseConfig())); } private LocalSearchPhaseConfig createLocalSearchPhaseConfig() { LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig(); UnionMoveSelectorConfig unionMoveSelectorConfig = new UnionMoveSelectorConfig(); ListChangeMoveSelectorConfig listChangeMoveSelectorConfig = new ListChangeMoveSelectorConfig(); listChangeMoveSelectorConfig.setFilterClass(MyListChangeMoveFilter.class); unionMoveSelectorConfig.addMoveSelectorConfig(listChangeMoveSelectorConfig); localSearchPhaseConfig.setMoveSelectorConfig(unionMoveSelectorConfig); return localSearchPhaseConfig; } }
3. Implement Your Move Filter
Your filter class must implement the MoveFilter interface, targeting the specific ListChangeMove type for type safety:
import org.optaplanner.core.api.score.director.ScoreDirector; import org.optaplanner.core.impl.heuristic.move.ListChangeMove; import org.optaplanner.core.impl.heuristic.move.filter.MoveFilter; public class MyListChangeMoveFilter implements MoveFilter<ListChangeMove<Visit>> { @Override public boolean accept(ScoreDirector<VehicleRoutingSolution> scoreDirector, ListChangeMove<Visit> move) { // Add your custom filtering logic here Visit movedVisit = move.getEntity(); Vehicle targetVehicle = (Vehicle) move.getToDestination(); // Example: Block moving low-priority visits to vehicles restricted to high-priority tasks return !targetVehicle.isRestrictedToHighPriority() || movedVisit.isHighPriority(); } }
内容的提问来源于stack exchange,提问作者Jon Britton

