【发布时间】:2013-12-20 22:43:19
【问题描述】:
如果我在 Java 中将对象传递给 Rhino,是否可以在该对象的类中使用注释来隐藏 Rhino JavaScripts 的方法\字段以使 JavaScripts 无法访问它们?或者有其他方法吗?
我做了一些研究,看起来我可以用 Scriptable 包装对象,但这似乎是一个比可能真正容易做到的更混乱的解决方案,因为它似乎是一个相当标准的功能。
谢谢。
【问题讨论】:
如果我在 Java 中将对象传递给 Rhino,是否可以在该对象的类中使用注释来隐藏 Rhino JavaScripts 的方法\字段以使 JavaScripts 无法访问它们?或者有其他方法吗?
我做了一些研究,看起来我可以用 Scriptable 包装对象,但这似乎是一个比可能真正容易做到的更混乱的解决方案,因为它似乎是一个相当标准的功能。
谢谢。
【问题讨论】:
我在 Rhino 引擎中找不到任何对此的支持(如果有人知道这样的事情,请说出来。)
也就是说,它很容易实现。您需要实现自己的 WrapFactory 和自己的 NativeJavaObject。您还需要调用创建您自己的 ContextFactory 以确保您的 WrapFactory 用于整个脚本引擎中使用的所有 Context 对象。这听起来像是很多工作......但实际上它实际上只是很多包装代码。这是我的实现的sn-p。缺少什么:是对 ContextFactory.initGlobal 的实际调用,以将全局 ContextFactory 设置为您的 ContextFactory 实现。
显然这段代码不是线程安全的
class ProtectedContextFactory extends ContextFactory
{
private static final ProtectedWrapFactory wrapper = new ProtectedWrapFactory();
@Override
protected Context makeContext()
{
Context c = super.makeContext();
c.setWrapFactory(wrapper);
return c;
}
}
class ProtectedWrapFactory extends WrapFactory
{
@Override
public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class<?> staticType)
{
return new ProtectedNativeJavaObject(scope, javaObject, staticType);
}
}
class ProtectedNativeJavaObject extends NativeJavaObject
{
private static final HashMap<Class<?>, ArrayList<String>> CLASS_PROTECTION_CACHE = new HashMap<Class<?>, ArrayList<String>>();
private ArrayList<String> m_protectedMembers;
public ProtectedNativeJavaObject(Scriptable scope, Object javaObject, Class<?> staticType)
{
super(scope, javaObject, staticType);
Class<?> clazz = javaObject != null ? javaObject.getClass() : staticType;
m_protectedMembers = CLASS_PROTECTION_CACHE.get(clazz);
if(m_protectedMembers == null)
m_protectedMembers = processClass(clazz);
}
private static ArrayList<String> processClass(Class<?> clazz)
{
ArrayList<String> protectedMethods = new ArrayList<String>();
CLASS_PROTECTION_CACHE.put(clazz, protectedMethods);
for(Method m : clazz.getMethods())
{
if(m.getAnnotation(ScriptHiddenMember.class) != null)
protectedMethods.add(m.getName());
}
for(Field f : clazz.getFields())
{
if(f.getAnnotation(ScriptHiddenMember.class) != null)
protectedMethods.add(f.getName());
}
return protectedMethods;
}
@Override
public boolean has(String name, Scriptable start)
{
if(m_protectedMembers.contains(name))
return false;
else
return super.has(name, start);
}
@Override
public Object get(String name, Scriptable start)
{
if(m_protectedMembers.contains(name))
return NOT_FOUND;
else
return super.get(name, start);
}
}
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ScriptHiddenMember {}
【讨论】:
使用接口并通过不将方法包含在接口中来隐藏方法将不起作用,脚本仍然能够访问接口中没有签名的方法,即使您将对象添加到引擎同时将其转换为接口.我发现隐藏方法的方法是将它们声明为私有、受保护或 (无修饰符);这使脚本无法访问。如果您使用 protected 或 (无修饰符),那么您仍然可以在其他类的代码中使用它们(假设您从有效位置调用它们),同时将它们隐藏在 JS 中。
对于变量,只要将它们声明为private、protected或(无修饰符),并且不包含任何getter方法,该变量就会被隐藏。即使没有 getter 方法,也可以访问公共变量。
使用这个界面:
public interface NodeInterface {
public int getPosition();
}
还有这个实现类:
public class Node implements NodeInterface{
private int x = 5;
public int y = 8;
private int position = 0;
public int getPosition(){return position;}
private String getString(){return "hello";}
public String getBing(){return "bing";}
}
这段代码的结果:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
NodeInterface node = (NodeInterface)new Node();
engine.put("node", node);
try {
engine.eval("println(node.x)");//undefined
engine.eval("println(node.y)");//8
engine.eval("println(node.position)");//0
engine.eval("println(node.getPosition())");//0
engine.eval("println(node.getBing());");//hello
engine.eval("println(node.getString())");//TypeError: Cannot find function getString.
} catch (ScriptException e1) {
e1.printStackTrace();
}
【讨论】:
为什么不尝试 Java 接口。对于将要公开给 Rhino 的每个类,定义一个接口,其中包含要公开的字段的 getter 和 setter,以及要公开的方法。
有关如何访问 Java 接口的更多信息,请访问 Rhino: Access Java interface variables in Javascript implementation
【讨论】: