Использование CDI для ввода факта в правило слюни - PullRequest
0 голосов
/ 06 марта 2020

Я хочу вставить факт в механизм правил Drools, который является компонентом CDI. Чтобы сделать это в обычном режиме java, я бы

@Inject
MyBean myBean;

Возможно ли @Inject в механизме правил? Нужна ли сварка как зависимость для этого?

Например, у меня есть правило, которое в настоящее время имеет следующие последствия (без CDI):

insert(new MyLibraryClass())

MyLibraryClass был обновлен и теперь включает инъекцию cdi, поэтому класс должен быть введен для правильной работы. Поскольку я хочу быть гибким в правилах, чтобы иметь возможность вставлять этот класс как факт или нет, и в какой момент времени я хочу выполнить вставку и создание объекта в механизме правил.

1 Ответ

0 голосов
/ 06 марта 2020

Просто вставьте его в класс, который вызывает правила (обычно Java), и передайте экземпляр в рабочую память при вызове правил.

Итак, если у вас есть, скажем, класс RuleInvoker, который вызывает правила ...

public class RuleInvoker {
  @Inject
  MyBean myBean;

  public void invokeRules() {

    KieSession kieSession = ...; 

    kieSession.insert( myBean ); // insert into working memory like any other fact

    // insert other facts, etc here, then fire rules

    kieSession.fireAllRules();

    // ... etc
  }
}

Я сделал инъекцию зависимостей, как это, используя Spring. Никаких дополнительных зависимостей, которые вы еще не используете, не требуется.


Для пояснения приведу небольшой пример с игрушкой.

Скажем, изначально у меня был класс Tax, который я Первоначально создавал новый экземпляр в правилах, а затем использовал в последующих правилах:

rule "Insert Tax"
when
  not (Tax())
then
  insert(new Tax())
end

rule "Apply Tax to Alcohol"
when
  $tax: Tax()
  $purchase: Purchase( includesAlcohol == true )
then
  $purchase.apply(tax, TaxType.ALCOHOL);
end

// Other rules eg. apply tax to lottery tickets, apply tax to groceries, etc.

Теперь я должен изменить свой дизайн, чтобы поддерживать разные схемы налогообложения для двух разных регионов. Эти схемы налогообложения будут введены; в этом случае я буду использовать аннотацию Spring @Autowired, но @Inject аналогичен.

Для моего надуманного примера я бы преобразовал Tax в интерфейс, а затем другие налоговые схемы просто реализовали бы этот интерфейс , Поскольку все правила написаны против налога, правила, таким образом, не нуждаются в изменении.

public interface Tax { 
  String getRegion();
  // other methods previously on Tax class
}
public class RegionATax implements Tax { ... }
public class RegionBTax implements Tax { ... }
public class RegionCTax implements Tax { ... }

Поскольку я больше не могу обновлять свои налоговые отчеты, мне нужно вывести их в контекст, поместив их в рабочую память вручную.

public class RuleInvoker {
  @Autowired
  List<Tax> taxSchemes; // injects all tax schemes (implementors of Tax interface)

  public void invokeRules(Purchase purchase) {

    KieSession kieSession = ...; 

    for (Tax tax : taxSchemes) {
      kieSession.insert( tax ); // insert into working memory like any other fact
    }

    // insert other facts, etc here, then fire rules
    kieSesion.insert(purchase);

    kieSession.fireAllRules();

    // ... etc
  }
}

И, наконец, правило «Включить налог» необходимо будет заменить правилом, которое определит, какую налоговую схему целесообразно использовать. В этом особенно надуманном примере, поскольку я не предоставлял себе никаких полезных методов, я просто собираюсь отозвать все несовместимые налоги, которые не соответствуют региону покупки.

rule "Filter incompatible tax regions"
salience 1
when
  Purchase( $region: region )
  $incompatibleTaxRegion: Tax( region != $region )
then
  retract( $incompatibleTaxRegion )
end

// Other rules remain the same since they're referencing the Tax interface

Я решил использовать отличие, чтобы гарантировать, что правило фильтра вызывается до обычных правил. Для этого лучше создать отдельную группу повестки дня.


В конце концов, вы не можете внедрять напрямую в контекст правила, потому что какая бы библиотека CDI вы не использовали, не управлять жизненным циклом рабочей памяти / правилом контекста. Вместо этого решение состоит в том, чтобы передать объект в правила, жизненный цикл которых контролируется , управляемым вашей библиотекой CDI.

Самый простой способ сделать это - просто передать бины напрямую в рабочую память в виде фактов или глобальных переменных. Другими решениями, которые я видел, являются передаваемые классы «инжекторов». Мне когда-то приходилось управлять набором правил, в котором контекст приложения Spring полностью передавался в правила (что-то вроде кошмара для работы с tbh.)

Если вы не хотите передавать бины напрямую, вы можете создать своего рода класс «инжектор» в качестве держателя бина, вставить , что , в класс вызывающего правила, а затем разрешить правила разберитесь, что им нужно. Я совсем не знаком с Weld, но если он делает доступным контекст приложения, который вы можете внедрить, вы также можете передать это в правила. Но все эти решения требуют, чтобы вы как-то корректировали правила, как минимум, чтобы удалить или обновить исходное insert( new MyLibraryClass()) правило.

...