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);
}
}
当然ながら、今後のバージョンでフィールド名が変更されることを考えると使い物にならない。う〜ん。