feat: 添加模板解析程序
This commit is contained in:
parent
fd44a594c0
commit
c391f8d322
@ -16,4 +16,27 @@
|
|||||||
<maven.compiler.target>8</maven.compiler.target>
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-beanutils</groupId>
|
||||||
|
<artifactId>commons-beanutils</artifactId>
|
||||||
|
<version>1.9.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.14</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-expression</artifactId>
|
||||||
|
<version>5.2.15.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -0,0 +1,99 @@
|
|||||||
|
package com.cicdi.notify.util;
|
||||||
|
|
||||||
|
import com.cicdi.notify.util.script.engine.DynamicScriptEngine;
|
||||||
|
import com.cicdi.notify.util.script.engine.spel.SpelParserEngine;
|
||||||
|
import org.apache.commons.beanutils.BeanUtilsBean2;
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表达式工具,用户解析表达式为字符串
|
||||||
|
*/
|
||||||
|
public class ExpressionUtils {
|
||||||
|
//表达式提取正则 ${.+?}
|
||||||
|
private static final Pattern PATTERN = Pattern.compile("(?<=\\$\\{)(.+?)(?=})");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认的表达式变量
|
||||||
|
*
|
||||||
|
* @return 变量集合
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> getDefaultVar() {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认的表达式变量并将制定的变量合并在一起
|
||||||
|
*
|
||||||
|
* @param var 要合并的变量集合
|
||||||
|
* @return 变量集合
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> getDefaultVar(Map<String, Object> var) {
|
||||||
|
Map<String, Object> vars = getDefaultVar();
|
||||||
|
vars.putAll(var);
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用默认的变量解析表达式
|
||||||
|
*
|
||||||
|
* @param expression 表达式字符串
|
||||||
|
* @return 解析结果
|
||||||
|
* @throws Exception 解析错误
|
||||||
|
* @see ExpressionUtils#analytical(String, Map)
|
||||||
|
*/
|
||||||
|
public static String analytical(String expression) throws Exception {
|
||||||
|
return analytical(expression, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析表达式,表达式使用{@link ExpressionUtils#PATTERN}进行提取<br>
|
||||||
|
* 如调用 analytical("http://${3+2}/test",var,"spel")<br>
|
||||||
|
*
|
||||||
|
* @param expression 表达式字符串
|
||||||
|
* @param context 变量
|
||||||
|
* @return 解析结果
|
||||||
|
*/
|
||||||
|
public static String analytical(String expression, Map<String, Object> context) {
|
||||||
|
if (!expression.contains("${")) {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TemplateParser.parse(expression, key -> {
|
||||||
|
if (key == null || "".equals(key)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpEL解析
|
||||||
|
DynamicScriptEngine engine = new SpelParserEngine();
|
||||||
|
String id = DigestUtils.md5Hex(key);
|
||||||
|
try {
|
||||||
|
if (!engine.compiled(id)) {
|
||||||
|
engine.compile(id, key);
|
||||||
|
}
|
||||||
|
if (engine.execute(id, context).isSuccess()) {
|
||||||
|
return String.valueOf(engine.execute(id, context).getIfSuccess());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表达式替换
|
||||||
|
if (!key.contains("#")) {
|
||||||
|
try {
|
||||||
|
Object fast = BeanUtilsBean2.getInstance().getPropertyUtils().getProperty(context, key);
|
||||||
|
if (fast != null) {
|
||||||
|
return fast.toString();
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
package com.cicdi.notify.util;
|
||||||
|
|
||||||
|
import org.apache.commons.beanutils.BeanUtilsBean;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xueye
|
||||||
|
*/
|
||||||
|
public class TemplateParser {
|
||||||
|
private static final char[] DEFAULT_PREPARE_START_SYMBOL = "${".toCharArray();
|
||||||
|
|
||||||
|
private static final char[] DEFAULT_PREPARE_END_SYMBOL = "}".toCharArray();
|
||||||
|
|
||||||
|
private char[] prepareStartSymbol = DEFAULT_PREPARE_START_SYMBOL;
|
||||||
|
|
||||||
|
private char[] prepareEndSymbol = DEFAULT_PREPARE_END_SYMBOL;
|
||||||
|
|
||||||
|
private String template;
|
||||||
|
|
||||||
|
private Object parameter;
|
||||||
|
|
||||||
|
public char[] getPrepareStartSymbol() {
|
||||||
|
return prepareStartSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrepareStartSymbol(char[] prepareStartSymbol) {
|
||||||
|
this.prepareStartSymbol = prepareStartSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char[] getPrepareEndSymbol() {
|
||||||
|
return prepareEndSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrepareEndSymbol(char[] prepareEndSymbol) {
|
||||||
|
this.prepareEndSymbol = prepareEndSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTemplate() {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemplate(String template) {
|
||||||
|
this.template = template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getParameter() {
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParameter(Object parameter) {
|
||||||
|
this.parameter = parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private char[] templateArray;
|
||||||
|
|
||||||
|
private int pos;
|
||||||
|
|
||||||
|
private char symbol;
|
||||||
|
|
||||||
|
private char[] newArr;
|
||||||
|
|
||||||
|
private int len = 0;
|
||||||
|
|
||||||
|
private byte prepareFlag = 0;
|
||||||
|
|
||||||
|
public void setParsed(char[] chars, int end) {
|
||||||
|
for (int i = 0; i < end; i++) {
|
||||||
|
char aChar = chars[i];
|
||||||
|
if (newArr.length <= len) {
|
||||||
|
newArr = Arrays.copyOf(newArr, len + templateArray.length);
|
||||||
|
}
|
||||||
|
newArr[len++] = aChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParsed(char... chars) {
|
||||||
|
setParsed(chars, chars.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
templateArray = template.toCharArray();
|
||||||
|
pos = 0;
|
||||||
|
newArr = new char[templateArray.length * 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPreparing() {
|
||||||
|
return prepareFlag > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPrepare() {
|
||||||
|
if (prepareStartSymbol[prepareFlag] == symbol) {
|
||||||
|
prepareFlag++;
|
||||||
|
}
|
||||||
|
if (prepareFlag >= prepareStartSymbol.length) {
|
||||||
|
prepareFlag = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPrepareEnd() {
|
||||||
|
for (char c : prepareEndSymbol) {
|
||||||
|
if (c == symbol) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean next() {
|
||||||
|
symbol = templateArray[pos++];
|
||||||
|
return pos < templateArray.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parse(Function<String, String> propertyMapping) {
|
||||||
|
init();
|
||||||
|
boolean inPrepare = false;
|
||||||
|
|
||||||
|
char[] expression = new char[128];
|
||||||
|
int expressionPos = 0;
|
||||||
|
|
||||||
|
while (next()) {
|
||||||
|
if (isPrepare()) {
|
||||||
|
inPrepare = true;
|
||||||
|
} else if (inPrepare && isPrepareEnd()) {
|
||||||
|
inPrepare = false;
|
||||||
|
setParsed(propertyMapping.apply(new String(expression, 0, expressionPos)).toCharArray());
|
||||||
|
expressionPos = 0;
|
||||||
|
} else if (inPrepare) {
|
||||||
|
expression[expressionPos++] = symbol;
|
||||||
|
} else if (!isPreparing()) {
|
||||||
|
setParsed(symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPrepareEnd() && expressionPos > 0) {
|
||||||
|
setParsed(propertyMapping.apply(new String(expression, 0, expressionPos)).toCharArray());
|
||||||
|
} else {
|
||||||
|
setParsed(symbol);
|
||||||
|
}
|
||||||
|
return new String(newArr, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String parse(String template, Object parameter) {
|
||||||
|
return parse(template, var -> {
|
||||||
|
try {
|
||||||
|
return BeanUtilsBean.getInstance().getProperty(parameter, var);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String parse(String template, Function<String, String> parameterGetter) {
|
||||||
|
TemplateParser parser = new TemplateParser();
|
||||||
|
parser.template = template;
|
||||||
|
return parser.parse(parameterGetter);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.cicdi.notify.util.script.engine;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface DynamicScriptEngine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引擎初始化
|
||||||
|
*
|
||||||
|
* @param contents 初始化内容
|
||||||
|
* @throws Exception 异常
|
||||||
|
*/
|
||||||
|
void init(String... contents) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译脚本
|
||||||
|
*
|
||||||
|
* @param id 脚本id
|
||||||
|
* @param code 脚本内容
|
||||||
|
* @return 编译是否成功
|
||||||
|
* @throws Exception 异常欣喜
|
||||||
|
*/
|
||||||
|
boolean compile(String id, String code) throws Exception;
|
||||||
|
|
||||||
|
ScriptContext getContext(String id);
|
||||||
|
|
||||||
|
boolean compiled(String id);
|
||||||
|
|
||||||
|
boolean remove(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行编译好的脚本
|
||||||
|
*
|
||||||
|
* @param id 编译后的id
|
||||||
|
* @param param 执行参数
|
||||||
|
* @return 执行结果
|
||||||
|
*/
|
||||||
|
ExecuteResult execute(String id, Map<String, Object> param);
|
||||||
|
|
||||||
|
void addGlobalVariable(Map<String, Object> vars);
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.cicdi.notify.util.script.engine;
|
||||||
|
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
|
public class ExecuteResult {
|
||||||
|
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
private Object result;
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResult(Object result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(this.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getIfSuccess() throws Exception {
|
||||||
|
if (!success) {
|
||||||
|
throw new ScriptException("");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.cicdi.notify.util.script.engine;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class ListenerSupportEngine implements DynamicScriptEngine {
|
||||||
|
|
||||||
|
private Map<String, Object> globalVariable;
|
||||||
|
|
||||||
|
protected Map<String, Object> getGlobalVariable() {
|
||||||
|
if (null == globalVariable) return new HashMap<>();
|
||||||
|
return new HashMap<>(globalVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addGlobalVariable(Map<String, Object> vars) {
|
||||||
|
globalVariable = vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.cicdi.notify.util.script.engine;
|
||||||
|
|
||||||
|
public class ScriptContext {
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
private final String md5;
|
||||||
|
|
||||||
|
public ScriptContext(String id, String md5) {
|
||||||
|
this.id = id;
|
||||||
|
this.md5 = md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMd5() {
|
||||||
|
return md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package com.cicdi.notify.util.script.engine.spel;
|
||||||
|
|
||||||
|
import com.cicdi.notify.util.script.engine.ExecuteResult;
|
||||||
|
import com.cicdi.notify.util.script.engine.ListenerSupportEngine;
|
||||||
|
import com.cicdi.notify.util.script.engine.ScriptContext;
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class SpelParserEngine extends ListenerSupportEngine {
|
||||||
|
|
||||||
|
protected final Map<String, SpelScriptContext> scriptContextCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
protected final ExpressionParser expressionParser = new SpelExpressionParser();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean compiled(String id) {
|
||||||
|
return scriptContextCache.containsKey(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(String... contents) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(String id) {
|
||||||
|
return scriptContextCache.remove(id) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScriptContext getContext(String id) {
|
||||||
|
return scriptContextCache.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean compile(String id, String code) {
|
||||||
|
scriptContextCache.put(id, new SpelScriptContext(id, DigestUtils.md5Hex(code), expressionParser.parseExpression(code)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecuteResult execute(String id, Map<String, Object> param) {
|
||||||
|
ExecuteResult result = new ExecuteResult();
|
||||||
|
SpelScriptContext scriptContext = scriptContextCache.get(id);
|
||||||
|
try {
|
||||||
|
if (scriptContext != null) {
|
||||||
|
param = new HashMap<>(param);
|
||||||
|
param.putAll(getGlobalVariable());
|
||||||
|
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext(param);
|
||||||
|
context.setVariable("u", new Date());
|
||||||
|
for (Map.Entry<String, Object> entry : param.entrySet()) {
|
||||||
|
context.setVariable(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
Expression script = scriptContext.getScript();
|
||||||
|
Object obj = script.getValue(context);
|
||||||
|
result.setSuccess(true);
|
||||||
|
result.setResult(obj);
|
||||||
|
} else {
|
||||||
|
result.setSuccess(false);
|
||||||
|
result.setResult(null);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SpelScriptContext extends ScriptContext {
|
||||||
|
private final Expression script;
|
||||||
|
|
||||||
|
public SpelScriptContext(String id, String md5, Expression script) {
|
||||||
|
super(id, md5);
|
||||||
|
this.script = script;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression getScript() {
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user