«1. Введение

В этом кратком руководстве мы собираемся интегрировать Drools с Spring. Если вы только начинаете работать с Drools, ознакомьтесь с этой вводной статьей.

2. Зависимости Maven

Давайте начнем с добавления следующих зависимостей в наш файл pom.xml:

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>7.0.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-spring</artifactId>
    <version>7.0.0.Final</version>
</dependency>

Последние версии можно найти здесь для drools-core и здесь для kie-spring.

3. Исходные данные

Давайте теперь определим данные, которые будут использоваться в нашем примере. Мы собираемся рассчитать стоимость поездки на основе пройденного расстояния и флага ночной доплаты.

Вот простой объект, который будет использоваться как факт:

public class TaxiRide {
    private Boolean isNightSurcharge;
    private Long distanceInMile;
    
    // standard constructors, getters/setters
}

Давайте также определим еще один бизнес-объект, который будет использоваться для представления тарифов:

public class Fare {
    private Long nightSurcharge;
    private Long rideFare;
    
    // standard constructors, getters/setters
}

Теперь давайте определим бизнес-правило для расчета Стоимость такси:

global com.baeldung.spring.drools.model.Fare rideFare;
dialect  "mvel"

rule "Calculate Taxi Fare - Scenario 1"
    when
        taxiRideInstance:TaxiRide(isNightSurcharge == false && distanceInMile < 10);
    then
      	rideFare.setNightSurcharge(0);
       	rideFare.setRideFare(70);
end

Как мы видим, определено правило для расчета общей стоимости проезда в такси.

Это правило принимает объект TaxiRide и проверяет, является ли атрибут isNightSurcharge ложным и значение атрибута DistanceInMile меньше 10, затем вычисляет стоимость проезда как 70 и устанавливает для свойства nightSurcharge значение 0.

Вычисленный результат устанавливается равным Тариф объект для дальнейшего использования.

4. Spring-интеграция

4.1. Spring Bean Configuration

Теперь давайте перейдем к интеграции Spring.

Мы собираемся определить класс конфигурации bean-компонента Spring, который будет отвечать за создание экземпляра bean-компонента TaxiFareCalculatorService и его зависимостей: услуги, предоставляемые Kie. KieServices извлекается с помощью KieServices.Factory.get().

@Configuration
@ComponentScan("com.baeldung.spring.drools.service")
public class TaxiFareConfiguration {
    private static final String drlFile = "TAXI_FARE_RULE.drl";

    @Bean
    public KieContainer kieContainer() {
        KieServices kieServices = KieServices.Factory.get();

        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        kieFileSystem.write(ResourceFactory.newClassPathResource(drlFile));
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        kieBuilder.buildAll();
        KieModule kieModule = kieBuilder.getKieModule();

        return kieServices.newKieContainer(kieModule.getReleaseId());
    }
}

Далее нам нужно получить KieContainer, который является заполнителем для всех объектов, необходимых для запуска механизма правил.

KieContainer создается с помощью других bean-компонентов, включая KieFileSystem, KieBuilder и KieModule.

Давайте приступим к созданию KieModule, который является контейнером всех ресурсов, необходимых для определения знаний о правилах, известных как KieBase.

После создания KieModule мы можем приступить к созданию KieContainer, который содержит KieModule, в котором была определена KieBase. KieContainer создается с помощью модуля:

KieModule kieModule = kieBuilder.getKieModule();

KieBase is a repository which contains all knowledge related to the application such as rules, processes, functions, type models and it is hidden inside KieModule. The KieBase can be obtained from the KieContainer.

4.2. Spring Service

KieContainer kContainer = kieServices.newKieContainer(kieModule.getReleaseId());

Давайте определим сервисный класс, который выполняет фактическую бизнес-логику, передавая объект Fact механизму для обработки результата:

Наконец, KieSession создается с использованием экземпляра KieContainer. Экземпляр KieSession — это место, куда можно вставлять входные данные. KieSession взаимодействует с механизмом для обработки фактической бизнес-логики, определенной в правиле на основе вставленных фактов.

@Service
public class TaxiFareCalculatorService {

    @Autowired
    private KieContainer kieContainer;

    public Long calculateFare(TaxiRide taxiRide, Fare rideFare) {
        KieSession kieSession = kieContainer.newKieSession();
        kieSession.setGlobal("rideFare", rideFare);
        kieSession.insert(taxiRide);
        kieSession.fireAllRules();
        kieSession.dispose();
        return rideFare.getTotalFare();
    }
}

Global (как и глобальная переменная) используется для передачи информации в движок. Мы можем установить Global с помощью setGlobal(“key”, value); в этом примере мы установили объект Fare как глобальный для хранения рассчитанной стоимости проезда на такси.

Как мы обсуждали в разделе 4, правило требует данных для работы. Мы вставляем факт в сеанс, используя kieSession.insert(taxiRide);

Как только мы закончим с настройкой входного факта, мы можем запросить движок для выполнения бизнес-логики, вызвав fireAllRules().

Наконец, нам нужно очистить сеанс, чтобы избежать утечки памяти, вызвав метод dispose().

5. Пример в действии

Теперь мы можем подключить контекст Spring и увидеть в действии, что Drools работает так, как ожидалось:

6. Заключение

@Test
public void whenNightSurchargeFalseAndDistLessThan10_thenFixWithoutNightSurcharge() {
    TaxiRide taxiRide = new TaxiRide();
    taxiRide.setIsNightSurcharge(false);
    taxiRide.setDistanceInMile(9L);
    Fare rideFare = new Fare();
    Long totalCharge = taxiFareCalculatorService.calculateFare(taxiRide, rideFare);
 
    assertNotNull(totalCharge);
    assertEquals(Long.valueOf(70), totalCharge);
}

В этой статье мы узнали о Drools Интеграция Spring с простым вариантом использования.

Как всегда, реализация примера и фрагменты кода доступны на GitHub.

«