1. ScriptEngine
JavaSE6中自带了JavaScript语言的脚本引擎,基于Mozilla的Rhino实现,可以通过三种方式查找脚本引擎:  
① 通过脚本名称获取: 
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); 
② 通过文件扩展名获取: 
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");  
③ 通过MIME类型来获取: 
ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript");  
JavaScript脚本中的println是Rhino引擎额外提供的打印控制台方法

JSEngineUtil

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

public class JSEngineUtil {
	/**
	 * 获取js引擎
	 */
	public static ScriptEngine getJavaScriptEngine() {
		ScriptEngineManager manager = new ScriptEngineManager();
//		ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
		ScriptEngine engine = manager.getEngineByName("JavaScript");
		return engine;
	}
	/**
	 * 执行表达式字符串脚本
	 * @param conditionExpression	表达式字符串脚本
	 * @param context	上下文容器
	 * @throws ScriptException
	 * @throws NoSuchMethodException
	 */
	public static boolean executeExpression(String conditionExpression, Map<String, Object> context) throws ScriptException, NoSuchMethodException {
		// conditionExpression 变量是$retValue形式,函数是@Substring形式
		Map<String, Object> inputVariables = new HashMap<String, Object>(); // 输入变量key-value包括变量和函数参数
		//处理变量
		List<String> variables = getVariables(conditionExpression);// 获取脚本中的变量名$retValue形式
		for (String var : variables) {
			Object value = context.get(var.substring(1)); // context中变量不是以$开头
			inputVariables.put(var, value);
		}
		ScriptEngine engine = getJavaScriptEngine();
		InputStream inputStream = JSEngineUtil.class.getResourceAsStream("function.js"); //暂时函数脚本统一在function.js内
		String streamString = getStreamString(inputStream);
		String removeChar = removeChar(conditionExpression, '@'); //表达式脚本中函数名去除@
		 // 在function.js字符串后拼接表达式脚本,一起编译,原理就是和js中一样,前面是函数,后面是表达式
		Object result = engine.eval(streamString + removeChar, new SimpleBindings(inputVariables));
		return (boolean) result;
	}
	/**
	 * 从表达式字符串中获取变量名  $retValue
	 * @param conditionExpression	表达式字符串
	 * @return
	 */
	public static List<String> getVariables(String conditionExpression){
		List<String> list = new ArrayList<String>();
		String regex = "\\$[A-Za-z_][A-Za-z0-9_]*";
		Pattern pattern =  Pattern.compile(regex);
		Matcher matcher = pattern.matcher(conditionExpression);
		while (matcher.find()) {
			list.add(matcher.group());
		}
		return list;
	}
	/**
	 * 从表达式字符串中获取函数名  @Substring
	 * @param conditionExpression 表达式字符串
	 * @return
	 */
	public static List<String> getFunctions(String conditionExpression){
		List<String> list = new ArrayList<String>();
		String regex = "@[A-Za-z][A-Za-z0-9_]*";
		Pattern pattern =  Pattern.compile(regex);
		Matcher matcher = pattern.matcher(conditionExpression);
		while (matcher.find()) {
			list.add(matcher.group());
		}
		return list;
	}
	/**
	 * 去除字符串中特殊字符,针对变量名 $retValue, 函数名@Substring
	 * @param conditionExpression
	 * @param ch  特殊字符
	 * @return
	 */
	public static String removeChar(String conditionExpression, char ch){
		for (int i = 0; i < conditionExpression.length(); i++) {
			if(conditionExpression.charAt(i) == ch && i+1 < conditionExpression.length()){
				char charAt = conditionExpression.charAt(i+1); // 下一个字符是[A-Za-z0-9_],这样就是变量名或者函数名
				if(Pattern.matches("[A-Za-z0-9_]", String.valueOf(charAt))){
					//去除@
					conditionExpression = conditionExpression.substring(0,i) + conditionExpression.substring(i+1);
				}
			}
		}
		return conditionExpression;
	}
	/**
	 * 从输入流中获取字符串
	 * @param in
	 * @return
	 */
	public static String getStreamString(InputStream in) {
		if (in != null) {
			try {
				//通过InputStreamReader包装类把字节输入流转为字符输入流
				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
				StringBuffer stringBuffer = new StringBuffer();
				String temp = null;
				while ((temp = bufferedReader.readLine()) != null) {
					stringBuffer.append(temp);
					stringBuffer.append("\r\n");
				}
				in.close();
				return stringBuffer.toString();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return null;
	}
}

function.js

/**
 * Created by bestm on 2019/11/8.
 * java动态执行js函数
 */
function Add(number1, number2){
    return number1 + number2;
}
function CompareTwo(number1, number2){
    return number1>number2;
}
function Substring(str, startPosition, length){
   return str.substring(startPosition, startPosition + length);
}
2. BeanShell

在jbpm3,4, Jmeter 中集成有beanshell
BeanShell是用Java写成的,一个小型的、免费的、可以下载、嵌入式的Java源代码解释器,具有对象脚本的特性。
BeanShell可以执行标准Java语句和表达式,以及另外自身的一些脚本命令和语法。
pom依赖

<dependency>
    <groupId>bsh</groupId>
    <artifactId>bsh</artifactId>
    <version>2.0b4</version>
    <type>pom</type>
</dependency>

function.bsh 脚本

//两个数相加,或者字符串拼接
Add(number1, number2){
    return number1 + number2;
}
//比较两个数大小
CompareTwo(number1, number2){
    return number1>number2;
}
//截取字符串
Substring(str, startPosition, length){
   return str.substring(startPosition, startPosition + length);
}
//字符串转为数字
StringToInt(number){
	return Integer.valueOf(number);
}

BeanShellUtil

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import bsh.EvalError;
import bsh.Interpreter;

public class BeanShellUtil {
	private static final String FUNCTION_SCRIPT = "function.bsh";
	/**
	 * 从表达式字符串中获取变量名 $retValue
	 * @param conditionExpression 表达式字符串
	 * @return
	 */
	public static List<String> getVariables(String conditionExpression) {
		List<String> list = new ArrayList<String>();
		String regex = "\\$[A-Za-z_][A-Za-z0-9_]*";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(conditionExpression);
		while (matcher.find()) {
			list.add(matcher.group());
		}
		return list;
	}
	/**
	 * 去除字符串中特殊字符,针对变量名 $retValue, 函数名@Substring
	 * @param conditionExpression
	 * @param ch  特殊字符
	 * @return
	 */
	public static String removeChar(String conditionExpression, char ch){
		for (int i = 0; i < conditionExpression.length(); i++) {
			if(conditionExpression.charAt(i) == ch && i+1 < conditionExpression.length()){
				char charAt = conditionExpression.charAt(i+1); // 下一个字符是[A-Za-z0-9_],这样就是变量名或者函数名
				if(Pattern.matches("[A-Za-z0-9_]", String.valueOf(charAt))){
					//去除@
					conditionExpression = conditionExpression.substring(0,i) + conditionExpression.substring(i+1);
				}
			}
		}
		return conditionExpression;
	}
	/**
	 * 执行表达式字符串脚本
	 * @param conditionExpression 表达式字符串脚本
	 * @param context 上下文容器
	 * @return
	 * @throws EvalError 
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 */
	public static boolean executeExpression(String conditionExpression, EsContext context) throws EvalError, FileNotFoundException, IOException {
		Interpreter interpreter = new Interpreter(); // 创建一个解释器对象
		// 处理变量
		List<String> variables = getVariables(conditionExpression);// 获取脚本中的变量名$retValue形式
		for (String var : variables) {
			Object value = context.get(var.substring(1)); // context中变量不是以$开头
			interpreter.set(var, value); // 设置变量值
		}
		String removeChar = removeChar(conditionExpression, '@'); //表达式脚本中函数名去除@
		//导入并执行一个脚本
		String path = BeanShellUtil.class.getResource(FUNCTION_SCRIPT).getPath();
		interpreter.source(path);
		Object result = interpreter.eval(removeChar); // 执行脚本
		return (boolean) result;
	}
	
	public static void main(String[] args) throws Exception{
		//调用
//		String conditionExpression = "$v1>$v2 && @Substring($branchNo,0,4)==\"4900\"";
		String conditionExpression = "@StringToInt($v1) > @StringToInt($v2) && @Substring($branchNo,0,4)==\"4900\"";
		EsContext context = new EsContext();
		context.set("v1", "3"); // 注意 不能为字符串,比较大小为数字
		context.set("v2", "2");
		context.set("branchNo", "4900");
		boolean executeExpression = executeExpression(conditionExpression, context);
		System.out.println(executeExpression);
		
		// 总结,BeanShell 表达式中单引号要转为双引号,比较大小,字符串会报错,要转为数字
		// 对比jsEngine 弱类型,特别适合脚本 由于BeanShell 的java强类型,虽然都是 动态脚本语言
	}
	
}

EsContext

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

public class EsContext implements Serializable{
	private static final long serialVersionUID = 4407682649837124658L;

	private Map<String, Object> content;
	
	public EsContext(){
		this.content = new HashMap<String, Object>(); // 注意因为效率问题,不采用有序的LinkedHashMap
	}
	public void setContent(Map<String, Object> content) {
		this.content = content;
	}

	public Map<String, Object> getContent(){
		return this.content;
	}
	
	public Object get(String name){
		if(this.content.containsKey(name)){
			Object object = this.content.get(name);
			return object;
		}
		return null;
	}
	
	public String getString(String name){
		if(this.content.containsKey(name)){
			Object object = this.content.get(name);
			if(object instanceof String){
				return (String)object;
			}
			throw new ClassCastException("it's a complex object type, not a string type");
		}
		return null;
	}
	
	/**
	 * 覆盖写入
	 * @param name
	 * @param value
	 */
	public void set(String name, Object value){
		this.content.put(name, value);
	}
	/**
	 * 不覆盖写入,如果有,不写入返回false,如果没有,写入返回true
	 * @param name
	 * @param value
	 * @return
	 */
	public boolean add(String name, Object value){
		if(this.content.containsKey(name)){
			return false;
		}
		this.content.put(name, value);
		return true;
	}


	@Override
	public String toString() {
		return "EsContext [content=" + content + "]";
	}
	
}
3. Aviator

谷歌的Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。
AviatorScript 5.x 支持脚本
pom依赖

<dependency>
    <groupId>com.googlecode.aviator</groupId>
    <artifactId>aviator</artifactId>
    <version>5.1.2</version>   <!-- jdk1.8 -->
</dependency>

hello.av

## com/yl/examples/hello.av

p("hello, AviatorScript! " + (a + b));

return "#{a * b + b} + #{a}";

测试

public static void test1() throws IOException {
		Expression exp = AviatorEvaluator.getInstance().compileScript("com/yl/examples/hello.av");
		Map<String, Object> newEnv = exp.newEnv("a", 2, "b", 6);
		Object result = exp.execute(newEnv);
		System.out.println(result);
	}
	//测试表达式
	public static void test2() throws IOException {
		Expression exp = AviatorEvaluator.getInstance().compile("a + b");
		Map<String, Object> newEnv = exp.newEnv("a", 2, "b", 6);
		Object result = exp.execute(newEnv);
		System.out.println(result);
		
	}