使用java.bean API检查非公共类的实例

时间:2012-04-30 18:04:58

标签: java clojure javabeans introspection

我正在尝试使用Clojure的(bean obj)来检索与对象相关的不可变地图。

在Clojure 1.4.0标准库中,大致如此实现(转换为不熟悉Clojure的人可以访问的伪代码):

import java.beans.PropertyDescriptor;
import java.beans.Introspector;

function introspect(Object obj) {
  Class clazz = obj.getClass();
  PropertyDescriptor descriptors[] =
    Introspector
    .getBeanInfo(clazz)
    .getPropertyDescriptors();
  Map retval = new HashMap();

  for(pd in descriptors) {
    name = pd.getName();
    method = pd.getReadMethod();
    if(method.getParameterTypes().length != 0)
      continue;
    retval.set(name, method.invoke(obj, nil));
  }
  /* the real implementation does more magic below here,
     but the above is sufficient for this question */
  return retval;
}

在大多数情况下,这很好用 - java.bean.Introspector不会在默认的BeanInfo实现中返回非公共方法。但是,当被检查的对象是非公共类的实例时,它会返回该类的公共方法 - 即使实际上不能在不引发IllegalArgumentException的情况下调用它们(“不能调用公共非公开课的方法“)。

如何解决这个问题?我正在查看java.lang.Class的文档,我没有看到一种明显的方法来确定一个类的权限,该权限不涉及java.lang.SecurityException的try / catch块...这不是'确切地说,这可能是我最好的做法。此外,在非公共类的方法实现公共接口的情况下,应该有一些机制来确定可以安全地调用此方法。

2 个答案:

答案 0 :(得分:2)

你可以发现一个类的修饰符,所以像这样的东西应该允许你检查一个对象是否是一个私有类的实例(没有经过严格测试)

public boolean isInstanceOfPrivateClass(Object o) {
    return Modifier.isPrivate(o.getClass().getModifiers());
}

答案 1 :(得分:1)

可以通过在继承树中搜索包含相同方法的公共类或接口来解决此问题。在Clojure中,这可以实现(尽管表现不佳)如下:

(defn- public-version-of-method [^Method method]
   "returns a Method built against a public interface or superclass
   declaring this method, or nil if none exists"
   (let [sig (method-sig method)]
     (loop [current-class (. method (getDeclaringClass))
            pending-supers (seq (supers current-class))]
       (if (and current-class
                (Modifier/isPublic (.getModifiers current-class))
                (some (fn [x] (= sig (method-sig x)))
                      (. current-class (getDeclaredMethods))))
         (. current-class (getDeclaredMethod
                           (.getName method)
                           (.getParameterTypes method)))
         (if pending-supers
           (recur (first pending-supers)
                  (next pending-supers))
           nil)))))

...然后在.invoke上调用(public-version-of-method m)而不是m(如果它返回非零值),或接受该方法无法公开访问返回零值。

(以上代码已作为CLJ-978的建议补丁的一部分在上游提交。