【问题标题】:How do I hide a class method from Rhino Script Engine?如何在 Rhino Script Engine 中隐藏类方法?
【发布时间】:2013-12-20 22:43:19
【问题描述】:

如果我在 Java 中将对象传递给 Rhino,是否可以在该对象的类中使用注释来隐藏 Rhino JavaScripts 的方法\字段以使 JavaScripts 无法访问它们?或者有其他方法吗?

我做了一些研究,看起来我可以用 Scriptable 包装对象,但这似乎是一个比可能真正容易做到的更混乱的解决方案,因为它似乎是一个相当标准的功能。

谢谢。

【问题讨论】:

    标签: java rhino


    【解决方案1】:

    我在 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 {}
    

    【讨论】:

      【解决方案2】:

      使用接口并通过不将方法包含在接口中来隐藏方法将不起作用,脚本仍然能够访问接口中没有签名的方法,即使您将对象添加到引擎同时将其转换为接口.我发现隐藏方法的方法是将它们声明为私有、受保护或 (无修饰符);这使脚本无法访问。如果您使用 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();
      }
      

      【讨论】:

        【解决方案3】:

        为什么不尝试 Java 接口。对于将要公开给 Rhino 的每个类,定义一个接口,其中包含要公开的字段的 getter 和 setter,以及要公开的方法。

        有关如何访问 Java 接口的更多信息,请访问 Rhino: Access Java interface variables in Javascript implementation

        【讨论】:

        • 我正在使用一个内部类(代表外部)来提供从脚本中对该外部对象的有限访问。问题是脚本可能希望在调用另一个 Java 方法时引用该外部对象。然后,被调用的 Java 方法将只有内部类的实例可以使用(因为调用脚本只能访问内部类。)现在我需要从内部类(它代表它的外部类到脚本)到外部类,以便可以不受方法限制地使用它。问题是要获得那个外部对象,我需要暴露一个...
        • 内部类中的方法(我想从脚本中隐藏,因此它无法访问外部对象),该方法将返回一个实例给调用的 java 方法可以使用的外部对象.如果我的解释难以理解,我可以举个例子。
        • 你考虑过用接口代替内部类吗?
        • 我使用内部类是因为脚本与对象的接口被简化,而不是与对象的直接接口。如果我使用接口,我仍然会遇到上述相同的问题。我需要向上转换对接口的引用——这很混乱\危险,并且不会比最初更进一步。我很确定有一个更简单的解决方案(比如告诉 Rhino 引擎不要公开某些方法。)
        猜你喜欢
        • 2013-07-14
        • 2011-08-15
        • 1970-01-01
        • 2014-12-01
        • 2016-07-22
        • 2012-01-17
        • 2020-06-20
        • 1970-01-01
        • 2011-09-14
        相关资源
        最近更新 更多