задний план
В процессе применения бизнес-системы иногда необходимо иметь дело с «часто меняющейся» частью. Эта часть требований может быть «бизнес-правилами» или «разной логикой обработки данных». Эта часть динамических правил часто требует быть настраиваемой и предъявлять определенные требования к производительности и производительности в реальном времени.
Java не является идеальным языком для решения задач с динамическими слоями, и на практике было установлено, что в основном это достигается следующими способами:
- язык выражения
- язык динамического/сценарного языка, например Groovy
- механизм правил
язык выражения
Унифицированный язык выражений Java, или сокращенно JUEL, представляет собой язык программирования специального назначения, который в основном используется в веб-приложениях Java для встраивания выражений в веб-страницы. Спецификаторы Java и группа технических экспертов в области Java Web разработали унифицированный язык выражений. Первоначально JUEL был включен в спецификацию JSP 2.1, JSR-245, а затем стал частью Java EE 7, где он был определен в JSR-341.
Основные реализации с открытым исходным кодом: OGNL, MVEL, SpEL, JUEL, Java Expression Language (JEXL), JEval, Jakarta JXPath и т. д.
Здесь мы в основном вводим МВЭЛ, ОГНЛ и СпЭЛ, которые больше используются на практике.
OGNL(Object Graph Navigation Library)
В библиотеке тегов Struts 2 выражения OGNL используются для доступа к данным объекта в ApplicationContext.Простой пример:
Foo foo = new Foo();
foo.setName("test");
Map<String, Object> context = new HashMap<String, Object>();
context.put("foo",foo);
String expression = "foo.name == 'test'";
try {
Boolean result = (Boolean) Ognl.getValue(expression,context);
System.out.println(result);
} catch (OgnlException e) {
e.printStackTrace();
}
MVEL
MVEL начинался как средство оценки выражений для проекта Майка Брока Valhalla, и по сравнению с оригинальными проектами OGNL, JEXL и JUEL он намного превосходит их по производительности, функциональности и простоте использования — особенно с точки зрения интеграции. Вместо того, чтобы пробовать другой язык JVM, он фокусируется на решении проблем со встроенными сценариями.
MVEL в основном используется в Drools и является неотъемлемой частью механизма правил Drools.
Синтаксис MVEL богат, он включает не только базовые выражения свойств, логические выражения, копирование переменных и вызовы методов, но также поддерживает определения функций, подробности см. в Руководстве по языку MVEL.
MVEL в основном имеет два вида режима интерпретации (режим интерпретации) и режим компиляции (режим компиляции) при выполнении языка:
-
Интерпретируемый режим (Interpreted Mode) — это динамическая интерпретация и выполнение без сохранения состояния, которые могут выполнять соответствующий скрипт без необходимости загрузочных выражений.
-
Компилируемый режим должен генерировать полностью нормализованное выражение в кеше перед его выполнением.
//режим интерпретации Foo foo = новый Foo(); foo.setName("тест"); Контекст карты = новый HashMap(); Строковое выражение = "foo.name == 'test'"; VariableResolverFactory functionFactory = new MapVariableResolverFactory (контекст); context.put("foo",foo); Логический результат = (логический) MVEL.eval(expression,functionFactory); System.out.println(результат);
// режим компиляции Foo foo = новый Foo();foo.setName("test"); Контекст карты = новый HashMap(); Строковое выражение = "foo.name == 'test'"; VariableResolverFactory functionFactory = new MapVariableResolverFactory(context);context.put("foo",foo); Сериализуемое выражение compileExpression = MVEL.compileExpression(expression); Логический результат = (логический) MVEL.executeExpression(compileExpression, context, functionFactory);
SpEL
SpEl (Spring Expression Language) — это мощный язык выражений, который поддерживает запросы и управление графами навигации по объектам во время выполнения. Его синтаксис похож на традиционный EL, но предоставляет дополнительную функциональность, в первую очередь вызовы функций и шаблонные функции для простых строк. SpEL похож на язык выражений OGNL, используемый в Struts2x.Он может создавать сложные выражения во время выполнения, получать доступ к свойствам графа объекта, вызовам методов объекта и т. д., а также может быть полностью интегрирован с функциями Spring, такими как настройка определений bean-компонентов.
SpEL в основном предоставляет базовые выражения, выражения, связанные с классами, выражения, связанные с коллекциями, и т. д. Дополнительные сведения см. в разделе Язык выражений Spring (SpEL).
Подобно OGNL, SpEL имеет базовые понятия, такие как выражение (выражение), Parser (парсер), EvaluationContext (контекст); подобно MVEL, SpEL также предоставляет два режима работы: режим интерпретации и режим компиляции.
//解释器模式
Foo foo = new Foo();
foo.setName("test");
// Turn on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true,true);
ExpressionParser parser = new SpelExpressionParser(config);
String expressionStr = "#foo.name == 'test'";
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("foo",foo);
Expression expression = parser.parseExpression(expressionStr);
Boolean result = expression.getValue(context,Boolean.class);
//编译模式
config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, RunSpel.class.getClassLoader());
parser = new SpelExpressionParser(config);
context = new StandardEvaluationContext();
context.setVariable("foo",foo);
expression = parser.parseExpression(expressionStr);
result = expression.getValue(context,Boolean.class);
двигатель правил
Некоторые движки правил: авиатор, легкие правила, слюни, эспер, сиддхи
aviator
AviatorScript — это высокопроизводительный, легкий язык сценариев, размещенный на JVM.
Сценарии использования включают в себя:
-
Суждение правил и механизм правил
-
расчет формулы
-
Динамическое управление сценариями
-
Совокупные данные ELT и т. д.
открытый класс Тест { public static void main(String[] args) { Строковое выражение = "a+(b-c)>100"; // компилируем выражение Выражение скомпилированоExp = AviatorEvaluator.compile(выражение);
Map<String, Object> env = new HashMap<>(); env.put("a", 100.3); env.put("b", 45); env.put("c", -199.100); // 执行表达式 Boolean result = (Boolean) compiledExp.execute(env); System.out.println(result);
} }
easy-rules
Easy Rules — это механизм правил Java.
Используйте POJO для определения правил:
@Rule(name = "weather rule", description = "if it rains then take an umbrella")
public class WeatherRule {
@Condition
public boolean itRains(@Fact("rain") boolean rain) {
return rain;
}
@Action
public void takeAnUmbrella() {
System.out.println("It rains, take an umbrella!");
}
}
Rule weatherRule = new RuleBuilder()
.name("weather rule")
.description("if it rains then take an umbrella")
.when(facts -> facts.get("rain").equals(true))
.then(facts -> System.out.println("It rains, take an umbrella!"))
.build();
Поддерживает использование языка выражений (MVEL/SpEL) для определения правил:
weather-rule.yml example:
name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
- "System.out.println(\"It rains, take an umbrella!\");"
MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));
Правила триггера:
public class Test {
public static void main(String[] args) {
// define facts
Facts facts = new Facts();
facts.put("rain", true);
// define rules
Rule weatherRule = ...
Rules rules = new Rules();
rules.register(weatherRule);
// fire rules on known facts
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, facts);
}
}
drools
An open source rule engine, DMN engine and complex event processing (CEP) engine for Java and the JVM Platform.
Определите правила:
import com.lrq.wechatDemo.domain.User // 导入类
dialect "mvel"
rule "age" // 规则名,唯一
when
$user : User(age<15 || age>60) //规则的条件部分
then
System.out.println("年龄不符合要求!");
end
Справочный пример:
public class TestUser {
private static KieContainer container = null;
private KieSession statefulKieSession = null;
@Test
public void test(){
KieServices kieServices = KieServices.Factory.get();
container = kieServices.getKieClasspathContainer();
statefulKieSession = container.newKieSession("myAgeSession");
User user = new User("duval yang",12);
statefulKieSession.insert(user);
statefulKieSession.fireAllRules();
statefulKieSession.dispose();
}
}
drools является относительно тяжелым механизмом правил и имеет собственное хранилище состояний, подробности см. в официальной документации.
esper
Esper is a component for complex event processing (CEP), streaming SQL and event series analysis, available for Java as Esper, and for .NET as NEsper.
один пример:
public class Test {
public static void main(String[] args) throws InterruptedException {
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPAdministrator admin = epService.getEPAdministrator();
String product = Apple.class.getName();
String epl = "select avg(price) from " + product + ".win:length_batch(3)";
EPStatement state = admin.createEPL(epl);
state.addListener(new AppleListener());
EPRuntime runtime = epService.getEPRuntime();
Apple apple1 = new Apple();
apple1.setId(1);
apple1.setPrice(5);
runtime.sendEvent(apple1);
Apple apple2 = new Apple();
apple2.setId(2);
apple2.setPrice(2);
runtime.sendEvent(apple2);
Apple apple3 = new Apple();
apple3.setId(3);
apple3.setPrice(5);
runtime.sendEvent(apple3);
}
}
siddhi
Siddhi is a cloud native
Streaming
and
Complex Event Processing
engine that understands Streaming SQL queries in order to capture events from diverse data sources, process them, detect complex conditions, and publish output to various endpoints in real time.
For example:
package io.siddhi.sample;
import io.siddhi.core.SiddhiAppRuntime;
import io.siddhi.core.SiddhiManager;
import io.siddhi.core.event.Event;
import io.siddhi.core.stream.input.InputHandler;
import io.siddhi.core.stream.output.StreamCallback;
import io.siddhi.core.util.EventPrinter;
/**
* The sample demonstrate how to use Siddhi within another Java program.
* This sample contains a simple filter query.
*/
public class SimpleFilterSample {
public static void main(String[] args) throws InterruptedException {
// Create Siddhi Manager
SiddhiManager siddhiManager = new SiddhiManager();
//Siddhi Application
String siddhiApp = "" +
"define stream StockStream (symbol string, price float, volume long); " +
"" +
"@info(name = 'query1') " +
"from StockStream[volume < 150] " +
"select symbol, price " +
"insert into OutputStream;";
//Generate runtime
SiddhiAppRuntime siddhiAppRuntime = siddhiManager.createSiddhiAppRuntime(siddhiApp);
//Adding callback to retrieve output events from stream
siddhiAppRuntime.addCallback("OutputStream", new StreamCallback() {
@Override
public void receive(Event[] events) {
EventPrinter.print(events);
//To convert and print event as a map
//EventPrinter.print(toMap(events));
}
});
//Get InputHandler to push events into Siddhi
InputHandler inputHandler = siddhiAppRuntime.getInputHandler("StockStream");
//Start processing
siddhiAppRuntime.start();
//Sending events to Siddhi
inputHandler.send(new Object[]{"IBM", 700f, 100L});
inputHandler.send(new Object[]{"WSO2", 60.5f, 200L});
inputHandler.send(new Object[]{"GOOG", 50f, 30L});
inputHandler.send(new Object[]{"IBM", 76.6f, 400L});
inputHandler.send(new Object[]{"WSO2", 45.6f, 50L});
Thread.sleep(500);
//Shutdown runtime
siddhiAppRuntime.shutdown();
//Shutdown Siddhi Manager
siddhiManager.shutdown();
}
}
И esper, и siddhi — это потоковые процессы, поддерживающие CEP и SQL, подробности см. в их официальной документации.
Динамический язык JVM
Groovy
Помимо широкого применения Gradle, еще одним масштабным применением Groovy должно быть использование динамического кода в сочетании с Java. Синтаксис Groovy настолько похож на Java, что большая часть кода Java также является правильным кодом Groovy. Код Groovy динамически преобразуется компилятором в байт-код Java. Благодаря своему характеру работы на JVM Groovy может использовать библиотеки, написанные на других языках Java.
Groovy можно рассматривать как язык, который добавляет динамические возможности в статичный мир Java, и Groovy реализует языковые функции, которых нет в Java:
- функциональный литерал;
- первоклассная поддержка коллекций;
- первоклассная поддержка регулярных выражений;
- Первоклассная поддержка xml;
Как язык на основе JVM, Groovy отличается от языка выражений на уровне языка, поэтому он более гибок в синтаксисе, чем выражение или язык. Когда Java вызывает Groovy, необходимо скомпилировать код Groovy в файл класса.
Groovy можно интегрировать с языком Java с помощью GroovyClassLoader, GroovyShell, GroovyScriptEngine и JSR223.
Пример динамической фильтрации json-объектов с помощью GroovyClassLoader:
public class GroovyFilter implements Filter {
private static String template = "" +
"package com.alarm.eagle.filter;" +
"import com.fasterxml.jackson.databind.node.ObjectNode;" +
"def match(ObjectNode o){[exp]}";
private static String method = "match";
private String filterExp;
private transient GroovyObject filterObj;
public GroovyFilter(String filterExp) throws Exception {
ClassLoader parent = Thread.currentThread().getContextClassLoader();
GroovyClassLoader classLoader = new GroovyClassLoader(parent);
Class clazz = classLoader.parseClass(template.replace("[exp]", filterExp));
filterObj = (GroovyObject)clazz.newInstance();
}
public boolean filter(ObjectNode objectNode) {
return (boolean)filterObj.invokeMethod(method, objectNode);
}
}
Каждый раз, когда Java вызывает код Groovy, Groovy будет скомпилирован в файл класса, поэтому во время вызова возникнут проблемы на уровне JVM. Например, использование метода parse GroovyShell приводит к заполнению области perm, а использование механизма загрузки GroovyClassLoader приводит к частым проблемам gc и заполнению CodeCache, что приводит к проблемам с отключением JIT и т. д. Связанные проблемы см. в общих разделах Интеграция Groovy и Java.
Наконец
Спасибо, что прочитали это. Если в статье есть какие-либо недостатки, вы можете указать на них. Наконец, вы можете обратить внимание на общедоступную учетную запись WeChat [Место сбора программистов Java], чтобы получить последние технические знания.