ScriptEngine解析js脚本 or 表达式

不得不说,ScriptEngine是个很强大的引擎,可以解析JS脚本,或者以JS的规则解析表达式。

javax.script,始于JDK1.6,不过现在只有sun实现的javascript的解析器,一般的用途主要是能解析通用的表达式,比如X >= 1(X作为参数传入)这样的表达式,也能利用js的函数语法,创造一个就像java的函数一样存在于内存中随时可以被调用的函数,更可以将js中的对象直接转换成java对象。

有时候出于各种原因我们需要用java模拟用户登陆一个网站,有的网站可能会把密码在前端用js加密后再传输到服务器。此时我们就需要找到对应的js方法,把它针对性的处理,再使用JAVA提供的ScriptEngineManager去执行提取处理后的js方法,得到我们想要的加密后的密码。

以上两种情况下,都可以用 javax.script 下的 ScriptEngine试试。

Script包下最主要的是ScriptEngineManager、ScriptEngine、CompiledScript和Bindings 4个类或接口。


20170715115237

ScriptEngineManager是一个工厂集合,可以通过name或tag的方式获取某个脚本的工厂并生成一个此脚本的ScriptEngine,目前只有javascript的工厂。通过工厂函数得到了ScriptEngine之后,就可以用这个对象来解析脚本字符串了,直接调用Object obj = ScriptEngine.eval(String script)即可,返回的obj为表达式的值,比如true、false或int值。

CompiledScript可以将ScriptEngine解析一段脚本的结果存起来,方便多次调用。只要将ScriptEngine用Compilable接口强制转换后,调用compile(String script)就返回了一个CompiledScript对象,要用的时候每次调用一下CompiledScript.eval()即可,一般适合用于js函数的使用。

Bindings的概念算稍微复杂点,我的理解Bindings是用来存放数据的容器。它有3个层级,为Global级、Engine级和Local级,前2者通过ScriptEngine.getBindings()获得,是唯一的对象,而Local Binding由ScriptEngine.createBindings()获得,很好理解,每次都产生一个新的。Global对应到工厂,Engine对应到ScriptEngine,向这2者里面加入任何数据或者编译后的脚本执行对象,在每一份新生成的Local Binding里面都会存在。

直接上代码吧:(代码中是以Spring中的项目中做的,所以把引擎对象的创建交给了spring容器管理)

Spring-context.xml 配置bean:ScriptEngineManager,

<bean id="scriptEngineManager" class="javax.script.ScriptEngineManager"></bean>

    <bean id="priceProvider" class="com.bj58.zbd.recycle.web.service.price.PriceProviderImpl" init-method="initScriptEngine"  ></bean>

其中 bean class : PriceProviderImpl.java 代码如下:

@Service
public class PriceProviderImpl extends BaseProvider implements PriceProvider  {
    @Autowired
    private ScriptEngineManager scriptEngineManager;


    private ScriptEngine scriptEngine;

    @Override
    public void initScriptEngine() {
        setScriptEngine(scriptEngineManager.getEngineByName("js"));
    }

	public ScriptEngine getScriptEngine() {
		return scriptEngine;
	}

	public void setScriptEngine(ScriptEngine scriptEngine) {
		this.scriptEngine = scriptEngine;
	}
}

关键代码:

scriptEngineManager.getEngineByName("js")

获取ScriptEngine。

使用的时候,直接用ScriptEngine即可:

@Autowired
    @Qualifier("priceProviderImpl")
    private PriceProviderImpl priceProvider;

    public double calculatePrice(){
        if (priceProvider.getScriptEngine() == null) {
            System.err.println("No engine for JavaScript");
            System.exit(1);
        }
        try {
            long s1 = System.currentTimeMillis();
            // 此行可变为从文件读入的表达式串。
            String s = "(500*2*0.8-50)/2";
            System.out.print(s + "=");
            Object result = priceProvider.getScriptEngine().eval(s);
            // 类型转换要修正下。上面那样写一直报错
            String str = result+"";
            System.out.println(str);
            System.out.println(System.currentTimeMillis()-s1);
            return Double.parseDouble(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;

    }

上面只是 用script引擎执行了一段表达式,有点大材小用了,下面贴出一段执行js函数的代码,大家参考下

try {
            ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
            Compilable compilable = (Compilable) engine;
            Bindings bindings = engine.createBindings(); //Local级别的Binding
            //定义函数并调用
            String script = "function add(op1,op2){return op1+op2} add(a, b)";
            //解析编译脚本函数
            CompiledScript JSFunction = compilable.compile(script);
            bindings.put("a", 1);bindings.put("b", 2); //通过Bindings加入参数
            Object result = JSFunction.eval(bindings);
            System.out.println(result); //调用缓存着的脚本函数对象,Bindings作为参数容器传入
        }catch (ScriptException e) {
            e.printStackTrace();
        }

 

转载请注明:刘召考的博客 » ScriptEngine解析js脚本 or 表达式

文章来源:

Author:goomoon
link:http://www.liuzk.com/325.html