Java低代码、配置化、DSL总结
技术能力对比
一、表达式引擎
应用场景总结:针对于需要进行脚本求值场景,性能上aviator比SpringEL要好一些,可能是1-2倍or10倍or更多,但是本身计算就是ns级别,所以在非高性能场景可以忽略不计。
标准接口定义:
import java.util.Map;
public interface ExpressionParser {
<T> T getValue(String expressionString, Map<String, Object> rootObject);
<T> T getValue(String expressionString);
}
① aviator
官方文档:https://github.com/killme2008/aviatorscript
引入:
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.2.1</version>
</dependency>
示例代码:
import com.boommapro.gaia.expression.ExpressionParser;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
import java.util.Map;
public class AviatorExpressionParser implements ExpressionParser {
private static AviatorEvaluatorInstance aviatorEvaluator = AviatorEvaluator.getInstance();
public <T> T getValue(String expression, Map<String, Object> context) {
//开启缓存
Expression expr = aviatorEvaluator.compile(expression, expression, true);
return (T) expr.execute(context);
}
@Override
public <T> T getValue(String expressionString) {
return getValue(expressionString, null);
}
}
② SpringEL
代码块参考:https://boommanpro.github.io/post/springelutil-code/
官方文档:https://docs.spring.io/spring-framework/reference/core/expressions.html
import com.boommapro.gaia.expression.ExpressionParser;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.expression.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeLocator;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SpringExpressionParser implements ExpressionParser {
private static final long serialVersionUID = 1L;
private StandardEvaluationContext context;
private org.springframework.expression.ExpressionParser expressionParser;
private final ConcurrentHashMap<String, Expression> EXPRESSION_MAP = new ConcurrentHashMap<>();
@Override
public <T> T getValue(String expressionString) {
return getValue(expressionString, null);
}
private static final SpringExpressionParser PARSER = new SpringExpressionParser();
public static SpringExpressionParser getInstance() {
return PARSER;
}
private SpringExpressionParser() {
setApplicationContext(new GenericApplicationContext());
}
@Override
@SuppressWarnings("all")
public <T> T getValue(String expressionString, Map<String, Object> rootObject) {
Expression expression = getExpression(expressionString);
return ((T) expression.getValue(context, rootObject));
}
private Expression getExpression(String expressionString) {
return EXPRESSION_MAP.computeIfAbsent(expressionString, s -> expressionParser.parseExpression(s));
}
@SuppressWarnings("all")
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (applicationContext instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext ac = (ConfigurableApplicationContext) applicationContext;
context = new StandardEvaluationContext();
context.addPropertyAccessor(new BeanExpressionContextAccessor());
context.addPropertyAccessor(new BeanFactoryAccessor());
context.addPropertyAccessor(new MapAccessor());
context.addPropertyAccessor(new EnvironmentAccessor());
context.setBeanResolver(new BeanFactoryResolver(ac.getBeanFactory()));
context.setTypeLocator(new StandardTypeLocator(ac.getBeanFactory().getBeanClassLoader()));
//初始化上下文
expressionParser = new SpelExpressionParser();
//注册默认工具方法
// registerFunc(context,xxx.class);
} else {
throw new ApplicationContextException("can't cast ConfigurableApplicationContext");
}
}
public void registerFunc(StandardEvaluationContext context, Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.getModifiers() == 9) {
String name = method.getName();
context.registerFunction(name, method);
}
}
}
public void registerFunc(StandardEvaluationContext context, String className) throws ClassNotFoundException {
Class<?> clazz = Class.forName(className);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.getModifiers() == 9) {
String name = method.getName();
context.registerFunction(name, method);
}
}
}
}
③ Groovy简化求值
本身代码管理和优化比较长,暂不提供。
二、动态脚本
应用场景总结:groovy适合轻量级引入,但是存在不兼容java lambda语法和部分语法不兼容等问题,在特殊情况下可以使用 Java Dynamic Class Loader
① Groovy
引入:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.17</version>
</dependency>
示例代码:
public class GroovyShellTest {
public static void main(String[] args) {
GroovyShell shell = new GroovyShell();
Binding binding = new Binding();
binding.setVariable("param", "param");
String script = "";
try {
shell.evaluate(script);
} catch (Exception e) {
throw new GroovyRuntimeException("groovy脚本执行异常", e);
} finally {
shell.resetLoadedClasses();
}
}
}
② Java Dynamic Class Loader
核心逻辑:通过javax.tools.JavaCompiler 编辑.java代码【注意类需要random后缀】,然后ClassLoader加载class后实例化。
代码较长,暂不提供。
三、插件化技术
① PF4J
官方文档:https://github.com/pf4j/pf4j
示例demo见:https://github.com/pf4j/pf4j/tree/master/demo
halo项目的最佳实践:https://github.com/halo-dev/halo
依赖引入:
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>3.10.0</version>
</dependency>