1. 程式人生 > >Java中原生解析JavaScript指令碼語言

Java中原生解析JavaScript指令碼語言

前言

由於一些需求,現在需要在Java中解析字串,做一些簡單的算數運算和邏輯運算,那麼最先想的是模板引擎這個東西,但是Java中的模板引擎是針對View層的,也就是JSP的,在Service層中使用不是太方便,因此選用了原生的JavaScript指令碼解析引擎。實際上Java原生支援解析大部分指令碼語言,像JavaScript,PHP,Python等。

那麼,先貼一下核心實現類的程式碼:

import java.io.FileNotFoundException;
import java.util.Map;

import javax.script.ScriptException;

public interface TempletEngineService {
	
	public Object eval(String script) throws ScriptException, FileNotFoundException;
	
	public Object eval(String script, Map<String, Object> vars) throws ScriptException, FileNotFoundException;
	
}

然後是實現類:


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

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

@Service
public class TempletEngineServiceImpl implements TempletEngineService {

	private ScriptEngine jsEngine;
	
	public TempletEngineServiceImpl() {
		ScriptEngineManager manager = new ScriptEngineManager();
		jsEngine = manager.getEngineByName("JavaScript");
	}
	
	@Override
	public Object eval(String script) throws ScriptException, FileNotFoundException {
		if (script == null || script.equals("")) {
			return null;
		}
		String jsfile = "/home/phw/Workspaces/MyEclipseProjects/gomoo-fu/src/com/gomoo/util/script/template.js";
		jsEngine.eval(new FileReader(new File(jsfile)));
		return jsEngine.eval(script);
	}
	
	@Override
	public Object eval(String script, Map<String, Object> vars) throws ScriptException, FileNotFoundException {
		if (script == null || script.equals("")) {
			return null;
		}
		if (vars == null || vars.isEmpty()) {
			for (Map.Entry<String, Object> entry: vars.entrySet()) {
				jsEngine.put(entry.getKey(), entry.getValue());
			}
		} 
		return eval(script);
	}

}

主要的核心類就是這兩個檔案,可以看到,用到的解析引擎是javax.script包下的引擎。

指令碼是自定義的一串符合JavaScript規則的字串,像簡單點的字串“5 + 6”、"9 - (6 + 5) * 5"等等,可以直接執行eval函式;若需要計算一些複雜的邏輯,那可以寫到你自定義的一個js檔案裡,然後在指令碼中執行那個函式;那再複雜一點,在函式中或者指令碼中帶自定義引數,這時就用到了eval(String script, Map vars)函數了。

下面是一些例子:

/**
	 * 簡單字串的測試
	 * @throws ScriptException 
	 * @throws FileNotFoundException 
	 */
	@Test
	public void testEval() throws FileNotFoundException, ScriptException {

		String script = "(65 - 5) / (3 * 6)";
		String script2 = "15 > 10 ? 15 : 10";
		String script3 = "15 > 10";
		
		Object val1 = templetEngineService.eval(script);
		System.out.println("指令碼1的結果為:" + val1);
		Object val2 = templetEngineService.eval(script2);
		System.out.println("指令碼2的結果為:" + val2);
		Object val3 = templetEngineService.eval(script3);
		System.out.println("指令碼3的結果為:" + val3);
		
	}

	/**
	 * 指令碼1的結果為:3.3333333333333335
	 * 指令碼2的結果為:15
	 * 指令碼3的結果為:true
	 */

	/**
	 * 帶函式的指令碼測試
	 * @throws ScriptException 
	 * @throws FileNotFoundException 
	 */
	@Test
	public void testEval2() throws FileNotFoundException, ScriptException {
		String script = "random()";
		String script2 = "random() + random()";
		Object val1 = templetEngineService.eval(script);
		System.out.println("指令碼1的結果為:" + val1);
		Object val2 = templetEngineService.eval(script2);
		System.out.println("指令碼2的結果為:" + val2);
	}
	
	/**
	 * 指令碼1的結果為:63.0
	 * 指令碼2的結果為:163.0
	 */

	/**
	 * 帶引數的指令碼測試
	 * @throws FileNotFoundException
	 * @throws ScriptException
	 */

	@Test
	public void testEval3() throws FileNotFoundException, ScriptException {
		
		String script = "random(min, max)";
		String script2 = "random(min, max) + min - max";
		
		Map<String, Object> vars = new HashMap<>();
		vars.put("min", 10);
		vars.put("max", 100);
		Object val1 = templetEngineService.eval(script, vars);
		System.out.println("指令碼1的結果為:" + val1);
		
		Map<String, Object> vars2 = new HashMap<>();
		vars.put("min", 100);
		vars.put("max", 1000);
		Object val2 = templetEngineService.eval(script2, vars2);
		System.out.println("指令碼2的結果為:" + val2);
	}

	/**
	 * 指令碼1的結果為:69.0
	 * 指令碼2的結果為:-65.0
	 */

/**
	 * 引數為類時的指令碼測試
	 * @throws FileNotFoundException
	 * @throws ScriptException
	 */
	@Test
	public void testEval4() throws FileNotFoundException, ScriptException {
		
		User user = new User();
		user.setRealName("phw");
		String script = "out(user)";
		Map<String, Object> vars = new HashMap<>();
		vars.put("user", user);
		Object val = templetEngineService.eval(script, vars);
		System.out.println("指令碼1的結果為:" + val);
	}
	
	/**
	 * 指令碼1的期望結果:phw
	 */

下面貼一下template.js,也就是我們預定義的Js function的檔案:

/**
 * 生成100內的隨機數
 */
function random() {
	return Math.floor(Math.random() * 100);
}

/**
 * 生成區間內的隨機整數[m, n]
 */
function random(minNum,maxNum){
  switch(arguments.length){ 
      case 1: 
          return parseInt(Math.random()*minNum+1,10); 
      break; 
      case 2: 
          return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10); 
      break; 
          default: 
              return 0; 
          break; 
  } 
}

function out(obj) {
	return obj.realName;
}

以上就是測試裡用到的js function函式,再說一下,指令碼不支援es6的語法,所以老老實實用var,不要用let。

以上。