Java对脚本语言的支持

Java对脚本语言的支持(JSR-223)

说白了,就是在Java环境中,可以运行脚本语言,例如JavaScript、Python等。

重要对象:

1. ScriptEngineManager:JDK注释解释的很明白:The ScriptEngineManager implements a discovery and instantiation mechanism for ScriptEngine classes and also maintains a collection of key/value pairs storing state shared by all engines created by the Manager.

就是说有2个主要的作用:

1). 提供一个发现创建脚本引擎的机制。

首先,ScriptEngineManager实例在当前classpath中搜索所有可见的jar包,查看每个jar包的/META-INF/services/目录下是否包含了javax.script.ScriptEngineFactory文件(这个文件由脚本引擎开发者提供,文件内容是一个实现了ScriptEngineFactory接口的类的完整类名);如果有这个文件,ScriptEngineManager会根据文件内容创建ScriptEngineFactory实例;再由这个ScriptEngineFactory实例创建ScriptEngine返回给用户。

private ServiceLoader<ScriptEngineFactory> getServiceLoader(final ClassLoader loader) {
    if (loader != null) {
        return ServiceLoader.load(ScriptEngineFactory.class, loader);
    } else {
        return ServiceLoader.loadInstalled(ScriptEngineFactory.class);
    }
}
public ScriptEngine getEngineByName(String shortName) {
    if (shortName == null) throw new NullPointerException();
    //look for registered name first
    Object obj;
    if (null != (obj = nameAssociations.get(shortName))) {
        ScriptEngineFactory spi = (ScriptEngineFactory)obj;
        try {
            ScriptEngine engine = spi.getScriptEngine();
            engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
            return engine;
        } catch (Exception exp) {
            if (DEBUG) exp.printStackTrace();
        }
    }

    for (ScriptEngineFactory spi : engineSpis) {
        List<String> names = null;
        try {
            names = spi.getNames();
        } catch (Exception exp) {
            if (DEBUG) exp.printStackTrace();
        }

        if (names != null) {
            for (String name : names) {
                if (shortName.equals(name)) {
                    try {
                        ScriptEngine engine = spi.getScriptEngine();
                        engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
                        return engine;
                    } catch (Exception exp) {
                        if (DEBUG) exp.printStackTrace();
                    }
                }
            }
        }
    }

    return null;
}

2). 维护一组键值对,这组键值对被所有由同一个ScriptEngineManager所创建出的ScriptEngines共享。

说白了就是那个Global Bindings,代码在ScriptEngineManager类的最后:

/** Global bindings associated with script engines created by this manager. */
private Bindings globalScope;

2. ScriptEngineFactory:

JDK注释:

ScriptEngineFactory is used to describe and instantiate ScriptEngines.
Each class implementing ScriptEngine has a corresponding factory that exposes metadata describing the engine class.
The ScriptEngineManager uses the service provider mechanism described in the Jar File Specification to obtain instances of all ScriptEngineFactories available in the current ClassLoader.

作用就是实例化ScriptEngine。

3. ScriptEngine:

JDK注释:

It includes methods that execute scripts, and ones that set and get values

The values are key/value pairs of two types.  The first type of pairs consists of those whose keys are reserved and defined in this specification or  by individual implementations.  The values in the pairs with reserved keys have specified meanings.

The other type of pairs consists of those that create Java language Bindings, the values are usually represented in scripts by the corresponding keys or by decorated forms of them.

2个作用:

1) 提供执行脚本的方法:eval

2) 提供set和get值的方法,这里的值分两种:系统保留的和Java代码中Bindings对象的。

4. ScriptContext

JDK注释:

The interface whose implementing classes are used to connect Script Engines with objects, such as scoped Bindings, in hosting applications.  Each scope is a set of named attributes whose values can be set and retrieved using the ScriptContext methods. ScriptContexts also expose Readers and Writers that can be used by the ScriptEngines for input and output.

2个作用:

1) 为ScriptEngine提供数据,例如:那两个不同作用域的bingdings对象。

2) 自定义reader和writer重定向输入输出,系统默认的输入输出都是控制台。

// SimpleScriptContext.java
public SimpleScriptContext() {
    engineScope = new SimpleBindings();
    globalScope = null;
    reader = new InputStreamReader(System.in);
    writer = new PrintWriter(System.out , true);
    errorWriter = new PrintWriter(System.err, true);
}

双向数据传递

Java语言与脚本语言通过bindings对象进行数据传递。

在ScriptContext中存在2种作用域的bindings对象,如下:

/**
 * This is the engine scope bindings.
 * By default, a <code>SimpleBindings</code> is used. Accessor
 * methods setBindings, getBindings are used to manage this field.
 * @see SimpleBindings
 */
protected Bindings engineScope;

/**
 * This is the global scope bindings.
 * By default, a null value (which means no global scope) is used. Accessor
 * methods setBindings, getBindings are used to manage this field.
 */
protected Bindings globalScope;

engineScope对象表示作用域是当前脚本引擎;globalScope对象表示作用域是同一ScriptManagerFactory创建出来的所有脚本引擎对象。不同的作用域的区别在于查找属性时的顺序不同,每种作用域都对应一个整数,整数值越小,优先级越高,这两种作用域对应的整数如下:

/**
 * EngineScope attributes are visible during the lifetime of a single
 * <code>ScriptEngine</code> and a set of attributes is maintained for each
 * engine.
 */
public static final int ENGINE_SCOPE = 100;

/**
 * GlobalScope attributes are visible to all engines created by same ScriptEngineFactory.
 */
public static final int GLOBAL_SCOPE = 200;

可见,在查找时,先从engineScope查找,找不到再从globalScope中查找。

访问bindings的方法:

1. ScriptEngine类提供了put和get方法对作用域为ENGINE_SCOPE的语言绑定对象进行操作:

// ========== AbstractEngineManager.java ==========

public Object get(String key) {

    Bindings nn = getBindings(ScriptContext.ENGINE_SCOPE);
    if (nn != null) {
        return nn.get(key);
    }

    return null;
}

public void put(String key, Object value) {

    Bindings nn = getBindings(ScriptContext.ENGINE_SCOPE);
    if (nn != null) {
        nn.put(key, value);
    }

}

public Bindings getBindings(int scope) {

    if (scope == ScriptContext.GLOBAL_SCOPE) {
        return context.getBindings(ScriptContext.GLOBAL_SCOPE);
    } else if (scope == ScriptContext.ENGINE_SCOPE) {
        return context.getBindings(ScriptContext.ENGINE_SCOPE);
    } else {
        throw new IllegalArgumentException("Invalid scope value.");
    }
}

// ========== SimpleScriptContext.java ==========

public Bindings getBindings(int scope) {
    if (scope == ENGINE_SCOPE) {
        return engineScope;
    } else if (scope == GLOBAL_SCOPE) {
        return globalScope;
    } else {
        throw new IllegalArgumentException("Illegal scope value.");
    }
}

2. ScriptContext对象中的setAttribute和getAttribute方法:

// ======== SimpleScriptContext.java ========

public void setAttribute(String name, Object value, int scope) {
    checkName(name);
    switch (scope) {

        case ENGINE_SCOPE:
            engineScope.put(name, value);
            return;

        case GLOBAL_SCOPE:
            if (globalScope != null) {
                globalScope.put(name, value);
            }
            return;

        default:
            throw new IllegalArgumentException("Illegal scope value.");
    }
}

public Object getAttribute(String name) {
    checkName(name);
    if (engineScope.containsKey(name)) {
        return getAttribute(name, ENGINE_SCOPE);
    } else if (globalScope != null && globalScope.containsKey(name)) {
        return getAttribute(name, GLOBAL_SCOPE);
    }

    return null;
}

public Object getAttribute(String name, int scope) {
    checkName(name);
    switch (scope) {

        case ENGINE_SCOPE:
            return engineScope.get(name);

        case GLOBAL_SCOPE:
            if (globalScope != null) {
                return globalScope.get(name);
            }
            return null;

        default:
            throw new IllegalArgumentException("Illegal scope value.");
    }
}

3. 手动创建bindings,jdk提供了手动创建bindings的方法。

// 1. 直接new一个SimpleBindings
Bindings bindings = new SimpleBindings();

// 2. 通过scriptEngine创建
Bindings binding = scriptEngine.createBindings();

// 创建完bindings就可以对其进行put或get操作。
bindings.put("name", "zs")
bindings.get("name");

// 在执行scriptEngine的eval方法时,有2中方法可以将手动创建的bindings对象参与到解析中。
// 1. 将手动创建的bindings对象设置到scriptContext中
context.setBindings(binding, ScriptContext.ENGINE_SCOPE);

// 2. 利用eval的重载方法
scriptEngine.eval("print('My name is ' + name)", bindings);

// eval的重载方法实际上是生成一个新的scriptContext对象,并将bindings设置进去。
public Object eval(String script, Bindings bindings) throws ScriptException {

    ScriptContext ctxt = getScriptContext(bindings);

    return eval(script , ctxt);
}

protected ScriptContext getScriptContext(Bindings nn) {

    SimpleScriptContext ctxt = new SimpleScriptContext();
    Bindings gs = getBindings(ScriptContext.GLOBAL_SCOPE);

    if (gs != null) {
        ctxt.setBindings(gs, ScriptContext.GLOBAL_SCOPE);
    }

    if (nn != null) {
        ctxt.setBindings(nn,
                ScriptContext.ENGINE_SCOPE);
    } else {
        throw new NullPointerException("Engine scope Bindings may not be null.");
    }

    ctxt.setReader(context.getReader());
    ctxt.setWriter(context.getWriter());
    ctxt.setErrorWriter(context.getErrorWriter());

    return ctxt;

}

JDK中JavaScript的引擎

1. JDK1.6使用的是Rhino。

2.JDK1.8使用了性能更好的Nashorn。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值