エンハンスされた型の認識

JavassistCGLIB等のバイトコードエンハンサは既存の型をオンザフライで拡張するのだが、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);
    }
}

当然ながら、今後のバージョンでフィールド名が変更されることを考えると使い物にならない。う〜ん。