2012-04-30 10 views
1

Clojureの(bean obj)を使用して、オブジェクトに関連する不変のマップを取得しようとしています。 Clojureの1.4.0標準ライブラリでjava.bean APIを使用して非公開クラスのインスタンスを検査する

が、これは大体そう(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実装の非公開メソッドは返されません。しかし、検査されているオブジェクトが非publicクラスのインスタンスである場合、実際に呼び出すことはできませんが、IllegalArgumentException( "非公開のpublicメソッドを呼び出すことはできません。パブリッククラス ")。

どのようにこれを修正できますか? java.lang.Classのドキュメントを見ていますが、try/catchブロックが含まれていないクラスのアクセス許可を判断する明白な方法は表示されませんjava.lang.SecurityException ...正確には私には当てはまりませんベストプラクティスさらに、非publicクラスのメソッドがパブリックインターフェイスを実装する場合、このメソッドが安全に呼び出せるかどうかを判断するためのいくつかのメカニズムが利用可能である必要があります。

答えて

2

あなたはクラスの修飾子を発見することができますので、このようなものは、あなたがオブジェクトはプライベートクラスのインスタンスであるかどうかを確認できるようにする必要があります(頻繁にテストされていない)

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

残念ながら、プライベートクラスがパブリックインターフェイスを実装する場合、この回答は、そのインターフェイスで定義されたメソッドがアクセス可能であることを判断するのには十分ではありません。 PropertyDescriptorからインタフェースを識別する方法と併せて、おそらくそれで十分でしょう。 –

+0

オブジェクトがインターフェイスを実装するクラス(定義によってパブリック)である場合、クラスのプライバシーに関係なくインターフェイス上のメソッドを呼び出すことができるはずです。それ以外はバグです。 Javaには、内部クラスが言語に追加されたために多くの不必要な手荷物があることに注意してください。プライベートクラスは内部クラスとしてのみ意味があります。あなたの例の構造に、私が紛失しているものがあるかもしれません。 – sw1nn

+0

私はいくつかのパブリックメソッドを持つ非公開クラスを持っています。これらのメソッドのいくつかは、インターフェイスを実装するクラスのおかげで公に呼び出すことができます。それらのパブリック・メソッドのいくつかは、パブリック・クラスまたはインタフェースでは定義されていないため、非パブリック・クラス自体でのみ定義されるため、呼び出すことができません。これを動かす実際のユースケースは、Jiraで使用されるAtlassianのCrowd製品の 'com.atlassian.crowd.embedded.ofbiz.OfBizUser'です。私は非公開のクラスでパブリックメソッドを持つのは意味がないと思うが、私の唯一のコントロールはそれを示唆するチケットを提出することだ。 –

1

この問題はで解決することができます継承ツリーを検索して、同じメソッドを含むパブリッククラスまたはインターフェイスを探します。 (それが非ゼロ値を返した場合)(public-version-of-method m)なくm.invokeを呼び出すか、その受け入れ次いで

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

...と:Clojureのは、これは次のように(貧弱な性能を有するにもかかわらず)に実装することができますこのメソッドがnil値を返す場合、メソッドはpublicにアクセスできません。

(上のコードは、CLJ-978の提案パッチの一部としてアップストリームに提出されています)。

+1

+1 - これは難しいことではありません:-)、参考用Apache commons-beanutilsはhttp://www.docjar.com/html/api/org/apache/commons/beanutils/MethodUtils.javaと似ています。 html#771 – sw1nn

+0

@ sw1nn Apache Commonsの実装を指摘していただきありがとうございます。 –