Статьи

Интеграция Drools с Spring против ручного создания правил

Часто в вашей работе вы можете столкнуться с проблемами, связанными с бизнес-логикой. Предположим, что у вас есть десятки правил (на данный момент в нашем проекте их более 50, и я привык работать над проектом, в котором было сотни таких правил), которые вы должны реализовать, разделить на несколько классов, подклассов. , абстракции и конечно же юнит тест. Это может быть сложным и трудоемким как для написания, так и для поддержки. Есть много способов решения этой проблемы, и я покажу вам один из них — JBoss Drools.

Drools — это библиотека, специально созданная для таких целей, как реализация правил. Как представлено в Википедии:


«Drools — это реализация механизма правил, основанная на алгоритме Чарльза Форги Rete, специально разработанная для языка Java». 

Он содержит механизм правил, который может обрабатывать правила, написанные на языке Drools (вы также можете предоставить правила в электронных таблицах Excel! — идеально подходит для поддержки и управления со стороны бизнеса).

В следующем примере мы рассмотрим способ интеграции JBoss Drools с Spring и пример решения аналогичной проблемы без Drools.

Давайте предположим, что у нас есть POJO — продукт, который может представлять медицинский или электронный продукт. 

Product.java

package pl.grzejszczak.marcin.drools.springintegration.model;
 
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
public class Product {
    private final TypeEnum typeEnum;
    private String productName;
 
    public Product(TypeEnum typeEnum) {
        this.typeEnum = typeEnum;
        productName = typeEnum.getSomeOutputString();
    }
 
    public TypeEnum getTypeEnum() {
        return typeEnum;
    }
 
    public String getProductName() {
        return productName;
    }
 
    public void setProductName(String productName) {
        this.productName = productName;
    }
}

Тип продукта определяется TypeEnum. Он также имеет outputString — давайте предположим, что он определяет марку продукта (или что вы хотите;))

TypeEnum.java

package pl.grzejszczak.marcin.drools.springintegration.enums;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
public enum TypeEnum {
    MEDICAL("medical", "aaabbbccc"), ELECTRONIC("electronic", "cccbbbaaa");
 
 
    private final String type;
    private final String someOutputString;
 
    private TypeEnum(String type, String someOutputString) {
        this.type = type;
        this.someOutputString = someOutputString;
    }
 
    public String getType() {
        return type;
    }
 
    public String getSomeOutputString() {
        return someOutputString;
    }
}

Допустим, что логика наших рулов такова, что в зависимости от типа перечисления мы хотим выполнить некоторую обработку (в нашем случае мы будем иметь один и тот же тип обработки — преобразование каждого ‘a’ в ‘b’ в выводе строка).

NoRulesProductServiceImpl.java

package pl.grzejszczak.marcin.drools.springintegration.service.nondrools;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
import pl.grzejszczak.marcin.drools.springintegration.service.Processor;
import pl.grzejszczak.marcin.drools.springintegration.service.ProductService;
 
import java.util.List;
 
import static com.google.common.collect.Lists.newArrayList;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
@Component("NoRulesProductServiceImpl")
public class NoRulesProductServiceImpl implements ProductService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(NoRulesProductServiceImpl.class);
 
    @Autowired
    @Qualifier("ProductProcessingService")
    private Processor<List<Product>> productProcessingService;
 
    @Override
    public void runProductLogic() {
        LOGGER.debug("Running product logic without Drools");
        Product medicalProduct = new Product(TypeEnum.MEDICAL);
        Product electronicProduct = new Product(TypeEnum.ELECTRONIC);
        LOGGER.debug("Running rules for products...");
        productProcessingService.process(newArrayList(medicalProduct, electronicProduct));
        LOGGER.debug("...finished running products.");
    }
}

Служба ProductProcessingService просматривает данные продукты, находит для них производителя и обрабатывает их.

ProductProcessingService.java

package pl.grzejszczak.marcin.drools.springintegration.service.nondrools;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import pl.grzejszczak.marcin.drools.springintegration.factory.ProcessingFactory;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
import pl.grzejszczak.marcin.drools.springintegration.service.Processor;
 
import java.util.List;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
@Component("ProductProcessingService")
public class ProductProcessingService implements Processor<List<Product>> {
 
    @Autowired
    @Qualifier("NoRulesProcessingFactory")
    private ProcessingFactory<Processor, Product> processingFactory;
 
    @Override
    public void process(List<Product> input) {
        for(Product product : input){
            Processor<Product> processor = processingFactory.createProcessingObject(product);
            processor.process(product);
        }
    }
}

ProcessingFactory — это интерфейс, который на основе заданного ввода (Product) создает выход (Processor), который впоследствии выполняет дальнейшую обработку. В нашем случае у нас есть фабрика, которая вместо использования множества ifs (представьте, что у нас более двух типов продуктов) использует карту, которая соответствует типу продукта с реализацией процессора. Как видите, мы меняем последовательность ifs в один get.

NoRulesProcessingFactory.java

package pl.grzejszczak.marcin.drools.springintegration.factory.nondrools;
 
import com.google.common.collect.ImmutableMap;
import org.springframework.stereotype.Component;
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum;
import pl.grzejszczak.marcin.drools.springintegration.factory.ProcessingFactory;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
import pl.grzejszczak.marcin.drools.springintegration.service.nondrools.ElectronicProductProcessingService;
import pl.grzejszczak.marcin.drools.springintegration.service.nondrools.MedicalProductProcessingService;
import pl.grzejszczak.marcin.drools.springintegration.service.Processor;
 
import java.util.Map;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
@Component("NoRulesProcessingFactory")
public class NoRulesProcessingFactory  implements ProcessingFactory<Processor, Product> {
 
    private static final Map<TypeEnum, Processor> PROCESSOR_MAP = new ImmutableMap.Builder<TypeEnum, Processor>().
            put(TypeEnum.MEDICAL, new MedicalProductProcessingService()).
            put(TypeEnum.ELECTRONIC, new ElectronicProductProcessingService()).
            build();
 
    /**
     * By using the map we don't have any ifs
     * @param inputObject
     * @return
     */
    @Override
    public Processor createProcessingObject(Product inputObject) {
        return PROCESSOR_MAP.get(inputObject.getTypeEnum());
    }
}

Я представлю здесь только один ProcessingService, поскольку другой точно такой же (я просто хотел показать концепцию).

ElectronicProductProcessingService.java

package pl.grzejszczak.marcin.drools.springintegration.service.nondrools;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
import pl.grzejszczak.marcin.drools.springintegration.service.Processor;
import pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil;
 
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
public class ElectronicProductProcessingService implements Processor<Product> {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(ElectronicProductProcessingService.class);
 
    @Override
    public void process(Product input) {
        checkArgument(TypeEnum.ELECTRONIC.equals(input.getTypeEnum()), "This processing service works only for electronic devices");
        checkArgument(!SomeUtil.replaceAWithB(input.getProductName()).equals(input.getProductName()), "The input has already been processed");
        LOGGER.debug("Running processing for Electronic Product");
        input.setProductName(SomeUtil.replaceAWithB(input.getProductName()));
        LOGGER.debug(format("ELECTRONIC rule applied without Drools, product name is now equal to [%s]", input.getProductName()));
    }
}

Как видите, здесь есть несколько вещей, которые необходимо протестировать и поддержать. Представьте, что произошло бы, если бы у нас было 100 типов с более сложными правилами, чем просто замена одной буквы другой. Так как мы можем сделать это с Drools? Давайте начнем с рассмотрения pom.xml.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>pl.grzejszczak.marcin</groupId>
 <artifactId>drools-spring-integration</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 
 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>1.6</maven.compiler.source>
  <maven.compiler.target>1.6</maven.compiler.target>
 
  <spring.version>3.1.1.RELEASE</spring.version>
 </properties>
 <repositories>
  <repository>
   <id>spring-release</id>
   <url>http://maven.springframework.org/release</url>
  </repository>
 </repositories>
 
 <dependencies>
  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>13.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
  <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-spring</artifactId>
            <version>5.4.0.Final</version>
        </dependency>
  <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
 
    </dependencies>
</project>

Давайте посмотрим на applicationContext.xml и drools-context.xml. Что касается первого, то, что мы делаем на самом деле, это просто показывает, где искать классы с точки зрения Spring и откуда импортировать контекст drools.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
    <import resource="classpath:drools-context.xml"/>
    <!-- Show Spring where to search for the beans (in which packages) -->
    <context:component-scan base-package="pl.grzejszczak.marcin.drools.springintegration" />
 
</beans>

Контекст для слюней. Посмотрите на псевдонимы для продуктовKSession. Предоставляя псевдоним, мы объединяем два потенциальных сеанса знаний в один. Один сеанс знаний определяется для одной базы знаний. Для базы знаний мы предоставляем список (в нашем случае только один ресурс) drl-файлов (мы могли бы предоставить таблицу Excel).

Drools-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:drools="http://drools.org/schema/drools-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://drools.org/schema/drools-spring http://drools.org/schema/drools-spring.xsd">
 
    <!-- KNOWLEDGE BASE FOR A GIVEN TYPE -->
    <drools:kbase id="productsKBase">
        <drools:resources>
            <drools:resource type="DRL"
                             source="classpath:rules/products.drl"/>
        </drools:resources>
    </drools:kbase>
 
    <drools:ksession id="productsKSession" name="productsKSession" type="stateless" kbase="productsKBase"/>
    <alias name="productsKSession" alias="electronicKSession"/>
    <alias name="productsKSession" alias="medicalKSession"/>
 
</beans>

Давайте проверим файл drl.

Мы определяем два правила — «МЕДИЦИНСКОЕ правило» и «ЭЛЕКТРОННОЕ правило». Для каждого случая мы проверяем:

  • имеет ли входной объект тип продукта 
  • имеет ли typeEnum медицинский или электронный 
  • не было ли у него изменено productName 

Затем мы обращаемся к продукту с помощью переменной $ product. Мы модифицируем продукт, используя 
ключевое слово
modify  (что означает, что все правила перепроверены — попробуйте удалить условие ‘productName! = ReplaceAWithB ($ product.typeEnum.someOutputString’ и у вас будет бесконечный цикл), установив новое productName. Посмотрите на все операции импорта и импорта функций. Вы можете выполнить статическую функцию (pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil.replaceAWithB или org.drools.core.util.StringUtils.isEmpty), импортировав ее. в файле drl.
В конце мы записываем,
что правило было применено.

products.drl

package pl.grzejszczak.marcin
 
import org.slf4j.LoggerFactory
import pl.grzejszczak.marcin.drools.springintegration.DroolsSpring
import pl.grzejszczak.marcin.drools.springintegration.model.Product
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum
 
import function pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil.replaceAWithB
import function org.drools.core.util.StringUtils.isEmpty
 
 
rule "MEDICAL rule"
 dialect "mvel"
when
     $product : Product( typeEnum == TypeEnum.MEDICAL, productName != replaceAWithB($product.typeEnum.someOutputString) )
then
     modify ($product) {productName = replaceAWithB($product.typeEnum.someOutputString)}
     LoggerFactory.getLogger(DroolsSpring.class).debug(String.format("MEDICAL rule applied, product name is now equal to [%s]", $product.productName))
end
 
rule "ELECTRONIC rule"
 dialect "mvel"
when
     $product : Product( typeEnum == TypeEnum.ELECTRONIC, productName != replaceAWithB($product.typeEnum.someOutputString) )
then
     modify ($product) {productName = replaceAWithB($product.typeEnum.someOutputString)}
     LoggerFactory.getLogger(DroolsSpring.class).debug(String.format("ELECTRONIC rule applied, product name is now equal to [%s]", $product.productName))
end

Мы используем фабрику, которая выбирает правильную StatelessKnowledgeSession — так как мы хотим только изменить объект ввода. Чтобы запустить правила Drools, мы запускаем метод execute со списком входных объектов. 

ProductServiceImpl.java

package pl.grzejszczak.marcin.drools.springintegration.service.drools;
 
import org.drools.runtime.StatelessKnowledgeSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum;
import pl.grzejszczak.marcin.drools.springintegration.factory.ProcessingFactory;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
import pl.grzejszczak.marcin.drools.springintegration.service.ProductService;
 
import static com.google.common.collect.Lists.newArrayList;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
@Component("ProductServiceImpl")
public class ProductServiceImpl implements ProductService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(ProductServiceImpl.class);
 
    @Autowired
    @Qualifier("ProductProcessingFactory")
    ProcessingFactory<StatelessKnowledgeSession, Product> processingFactory;
 
    @Override
    public void runProductLogic() {
        LOGGER.debug("Running product logic");
        Product medicalProduct = new Product(TypeEnum.MEDICAL);
        Product electronicProduct = new Product(TypeEnum.ELECTRONIC);
        StatelessKnowledgeSession statelessKnowledgeSession = processingFactory.createProcessingObject(medicalProduct);
        LOGGER.debug("Running rules for products...");
        statelessKnowledgeSession.execute(newArrayList(medicalProduct, electronicProduct));
        LOGGER.debug("...finished running products.");
    }
}

Теперь давайте посмотрим, как реализована фабрика. Мы используем псевдонимы в applicationContext.xml

ProductProcessingFactory.java

package pl.grzejszczak.marcin.drools.springintegration.factory.drools;
 
import org.drools.runtime.StatelessKnowledgeSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import pl.grzejszczak.marcin.drools.springintegration.factory.ProcessingFactory;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
@Component("ProductProcessingFactory")
public class ProductProcessingFactory implements ProcessingFactory<StatelessKnowledgeSession, Product> {
 
    @Autowired
    private ApplicationContext applicationContext;
 
    @Override
    public StatelessKnowledgeSession createProcessingObject(Product inputObject) {
        return (StatelessKnowledgeSession)applicationContext.getBean(inputObject.getTypeEnum().getType() + "KSession");
    }
}

Теперь, как мы можем проверить, работает ли это? У меня есть два модульных теста, которые доказывают это (они не работают в полной изоляции, но они функциональны обоих подходов). Начиная с теста для ручного создания правил теста.

NoRulesProductServiceImplTest.java

package pl.grzejszczak.marcin.drools.springintegration.service;
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
import pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil;
 
import java.util.List;
 
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class NoRulesProductServiceImplTest {
 
    @Autowired
    @Qualifier("ProductProcessingService")
    private Processor<List<Product>> productProcessingService;
 
 
    /**
     * Test is not run in perfect isolation - the purpose is to show the outcome of processing without Drools
     *
     * @throws Exception
     */
    @Test
    public void testRunProductLogic() throws Exception {
        Product medicalProduct = new Product(TypeEnum.MEDICAL);
        Product electronicProduct = new Product(TypeEnum.ELECTRONIC);
        String initialMedicalProductName = medicalProduct.getProductName();
        String initialElectronicProduct = electronicProduct.getProductName();
        System.out.println(format("Initial productName for Medical [%s]", medicalProduct.getProductName()));
        System.out.println(format("Initial productName for Electronic [%s]", electronicProduct.getProductName()));
 
        productProcessingService.process(newArrayList(medicalProduct, electronicProduct));
 
        String finalMedicalProduct = medicalProduct.getProductName();
        String finalElectronicProduct = electronicProduct.getProductName();
        assertNotSame(finalMedicalProduct, initialMedicalProductName);
        assertNotSame(finalElectronicProduct, initialElectronicProduct);
        assertEquals(SomeUtil.replaceAWithB(initialMedicalProductName), finalMedicalProduct);
        assertEquals(SomeUtil.replaceAWithB(initialElectronicProduct), finalElectronicProduct);
 
        System.out.println(format("Final productName for Medical [%s]", medicalProduct.getProductName()));
        System.out.println(format("Final productName for Electronic [%s]", electronicProduct.getProductName()));
    }
}

И юнит-тест для подхода Drools.

ProductServiceImplTest.java

package pl.grzejszczak.marcin.drools.springintegration.service;
 
import org.drools.runtime.StatelessKnowledgeSession;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import pl.grzejszczak.marcin.drools.springintegration.enums.TypeEnum;
import pl.grzejszczak.marcin.drools.springintegration.factory.ProcessingFactory;
import pl.grzejszczak.marcin.drools.springintegration.model.Product;
import pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil;
 
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
 
/**
 * Created with IntelliJ IDEA.
 * User: mgrzejszczak
 * Date: 14.01.13
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class ProductServiceImplTest {
 
    @Autowired
    @Qualifier("ProductProcessingFactory")
    ProcessingFactory<StatelessKnowledgeSession, Product> processingFactory;
 
    /**
     * Test is not run in perfect isolation - the purpose is to show the outcome of processing with Drools
     * @throws Exception
     */
    @Test
    public void testRunProductLogic() throws Exception {
        Product medicalProduct = new Product(TypeEnum.MEDICAL);
        Product electronicProduct = new Product(TypeEnum.ELECTRONIC);
        String initialMedicalProductName = medicalProduct.getProductName();
        String initialElectronicProduct = electronicProduct.getProductName();
        System.out.println(format("Initial productName for Medical [%s]", medicalProduct.getProductName()));
        System.out.println(format("Initial productName for Electronic [%s]", electronicProduct.getProductName()));
 
        StatelessKnowledgeSession statelessKnowledgeSessionForMedical = processingFactory.createProcessingObject(medicalProduct);
        StatelessKnowledgeSession statelessKnowledgeSessionForElectronic = processingFactory.createProcessingObject(electronicProduct);
        assertTrue(statelessKnowledgeSessionForMedical == statelessKnowledgeSessionForElectronic);
        System.out.println("References for stateless sessions are the same, executing rules...");
 
        statelessKnowledgeSessionForMedical.execute(newArrayList(medicalProduct, electronicProduct));
 
        String finalMedicalProduct = medicalProduct.getProductName();
        String finalElectronicProduct = electronicProduct.getProductName();
        assertNotSame(finalMedicalProduct, initialMedicalProductName);
        assertNotSame(finalElectronicProduct, initialElectronicProduct);
        assertEquals(SomeUtil.replaceAWithB(initialMedicalProductName), finalMedicalProduct);
        assertEquals(SomeUtil.replaceAWithB(initialElectronicProduct), finalElectronicProduct);
 
        System.out.println(format("Final productName for Medical [%s]", medicalProduct.getProductName()));
        System.out.println(format("Final productName for Electronic [%s]", electronicProduct.getProductName()));
    }
}

Теперь давайте посмотрим на журналы — посмотрим, что «Выполнение некоторой логики» происходило 6 раз для Drools, поскольку при изменении объекта правила проходят повторную проверку и повторный запуск:

org.springframework.context.support.ClassPathXmlApplicationContext:495 Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@743399: startup date [Tue Jan 15 16:32:30 CET 2013]; root of context hierarchy
org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 Loading XML bean definitions from class path resource [applicationContext.xml]
org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 Loading XML bean definitions from class path resource [drools-context.xml]
[main] org.springframework.beans.factory.support.DefaultListableBeanFactory:557 Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3b1d04: defining beans [productsKBase,productsKSession,ProductProcessingFactory,NoRulesProcessingFactory,ProductServiceImpl,NoRulesProductServiceImpl,ProductProcessingService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
pl.grzejszczak.marcin.drools.springintegration.service.drools.ProductServiceImpl:32 Running product logic
pl.grzejszczak.marcin.drools.springintegration.service.drools.ProductServiceImpl:36 Running rules for products...
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.DroolsSpring:? ELECTRONIC rule applied, product name is now equal to [cccbbbbbb]
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.DroolsSpring:? MEDICAL rule applied, product name is now equal to [bbbbbbccc]
pl.grzejszczak.marcin.drools.springintegration.service.drools.ProductServiceImpl:38 ...finished running products.
pl.grzejszczak.marcin.drools.springintegration.service.nondrools.NoRulesProductServiceImpl:33 Running product logic without Drools
pl.grzejszczak.marcin.drools.springintegration.service.nondrools.NoRulesProductServiceImpl:36 Running rules for products...
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.service.nondrools.MedicalProductProcessingService:26 Running processing for Medical Product
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.service.nondrools.MedicalProductProcessingService:28 MEDICAL rule applied without Drools, product name is now equal to [bbbbbbccc]
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.service.nondrools.ElectronicProductProcessingService:26 Running processing for Electronic Product
pl.grzejszczak.marcin.drools.springintegration.utils.SomeUtil:19 Executing some logic
pl.grzejszczak.marcin.drools.springintegration.service.nondrools.ElectronicProductProcessingService:28 ELECTRONIC rule applied without Drools, product name is now equal to [cccbbbbbb]
pl.grzejszczak.marcin.drools.springintegration.service.nondrools.NoRulesProductServiceImpl:38 ...finished running products.

Виола! Таким образом, вы можете написать некоторые правила для Drools, которые могут сэкономить много времени и усилий применительно к бизнес-логике. Вы можете найти источники здесь в моем
хранилище BitBucket .