简体   繁体   中英

Inspecting instances of non-public classes using java.bean API

I'm attempting to use Clojure's (bean obj) to retrieve an immutable map related to an object.

In the Clojure 1.4.0 standard library, this is implemented roughly like so (translating into pseudocode intended accessible to folks unfamiliar with 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;
}

For the most part, this works fine -- java.bean.Introspector doesn't return non-public methods in its default BeanInfo implementation. However, when the object being inspected is an instance of a non-public class, it returns public methods on that class -- even though these can't actually be invoked without raising an IllegalArgumentException ("Can't call public method of non-public class").

How can this be fixed? I'm looking through the documentation for java.lang.Class , and I don't see an obvious way to determine a class's permissions that doesn't involve a try/catch block for java.lang.SecurityException ... which doesn't exactly strike me as likely to be a best practice. Moreover, in the case where a method on a non-public class implements a public interface, some mechanism should be available to determine that this method can be safely called.

You can discover the modifiers on a class, so something like this should allow you to check whether an object is an instance of a private class (not heavily tested)

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

This issue can be solved by searching the inheritance tree for a public class or interface containing the same method. In Clojure, this can be implemented (albeit with poor performance) as follows:

(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)))))

...and then calling .invoke on (public-version-of-method m) rather than m (if it returns a non-nil value), or accepting that the method is not publicly accessible if this method returns a nil value.

(The above code has been submitted upstream as part of a proposed patch for CLJ-978 ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM