エンハンスされた型の認識
JavassistやCGLIB等のバイトコードエンハンサは既存の型をオンザフライで拡張するのだが、java.reflect.Proxy等とは違い、拡張した後は通常のClass型であり、名前以外は通常の型と全く同じである。
では、エンハンサで拡張された型を判定する方法は無いのだろうか?
ちなみにCGLIB2.2のEnhancerには次のようなメソッドが存在している。
public static boolean isEnhanced(Class type) { try { getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME); return true; } catch (NoSuchMethodException e) { return false; } }
このメソッドによると、CGLIBは特定の名前のセッターメソッド(この場合は"CGLIB$SET_THREAD_CALLBACKS")か取得できたクラスは、自身が拡張した型としている。これは完全に実装依存の判定方法だが、このようにするしかないだろう。
一方JavassistのProxyFactoryには同様のメソッドは無かった。CGLIBのように特定のメソッドやフィールドを取得しているメソッドは無いかと探してみたが、
private static final String METHOD_FILTER_FIELD = "_method_filter"; private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; static MethodFilter getFilter(Class clazz) { return (MethodFilter)getField(clazz, METHOD_FILTER_FIELD); } static MethodHandler getHandler(Class clazz) { return (MethodHandler)getField(clazz, DEFAULT_INTERCEPTOR); }
と、それっぽいものを見つけたのだが残念ながらいずれもパッケージスコープだ。なので、取敢えず以下のように同様のメソッドを用意してみたのだが、
private static final String METHOD_FILTER_FIELD = "_method_filter"; private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; public static boolean isEnhanced(Class type) { try { return ( getField(type, METHOD_FILTER_FIELD) != null || getField(type, DEFAULT_INTERCEPTOR != null ); } catch (NoSuchFieldException e) { return false; } } private static Object getField(Class clazz, String fieldName) { try { Field f = clazz.getField(fieldName); f.setAccessible(true); Object value = f.get(null); f.setAccessible(false); return value; } catch (Exception e) { throw new RuntimeException(e); } }
当然ながら、今後のバージョンでフィールド名が変更されることを考えると使い物にならない。う〜ん。