Javassistで対象クラスのアノテーション情報を取得

Javassitはいわゆるバイトコード操作ライブラリですが、クラスの解析も出来るんじゃないかなと思いました。
ということで、やってみた記事になります。

解析対象クラス

このクラスのメソッドに付与されているアノテーションを取得してみます。
雑いテストクラスで正直すまんこってす。

package oreore.tool.detector.target;

public class Target {
    Date date;
    LocalDate localDate;

    @Deprecated
    public Date getDate() {
        return date;
    }

    @Transient
    public void setDate(Date date) {
        this.date = date;
    }

    @Addressing
    public LocalDate getLocalDate() {
        return localDate;
    }

    public void setLocalDate(LocalDate localDate) {
        this.localDate = localDate;
    }
}

アノテーションを解析するクラス

ちょっとOOPっぽい形にしてみました。
まず親がこれ。

public abstract class Detector {
    CtClass loadClass(final String fqcn) {
        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = null;
        try {
            ctClass = classPool.get(fqcn);
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        return ctClass;
    }

    protected abstract DetectorResult analyze(final CtClass ctClass, final DetectorResult detectorResult);

    public DetectorResult invoke(final String fqcn) {
        final CtClass ctClass = loadClass(fqcn);
        final DetectorResult detectorResult = new DetectorResult();
        analyze(ctClass, detectorResult);
        return detectorResult;
    }

    public static class DetectorResult {
        private Map<String, Object> information = new HashMap<>();

        public Map<String, Object> getInformation() {
            return information;
        }

        public void addInformation(final String key, final List list) {
            this.information.put(key, list);
        }
    }
}

そして、アノテーション解析するクラスです。
こいつの中にpsmvを定義しているのはご愛嬌。(面倒くさかっただけともいう)

public class AnnotationDetector extends Detector {
    @Override
    protected DetectorResult analyze(CtClass ctClass, DetectorResult detectorResult) {
        final Stream<CtMethod> ctMethodStream = Arrays.stream(ctClass.getMethods());
        ctMethodStream.forEach(method -> {
            try {
                final List<Object> annotations = Arrays.asList(method.getAnnotations());
                detectorResult.addInformation(method.getName(),
                        annotations);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        });
        return detectorResult;
    }

    public static void main(String[] args) {
        final Detector annotationDetector = new AnnotationDetector();
        final DetectorResult result = annotationDetector.invoke("oreore.tool.detector.target.Target");
        for (Map.Entry<String, Object> entry: result.getInformation().entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }

    }
}

実行結果

ふむ。

getClass : []
wait : []
notifyAll : []
notify : []
getLocalDate : [@javax.xml.ws.soap.Addressing]
setLocalDate : []
hashCode : []
equals : []
clone : []
setDate : [@java.beans.Transient]
finalize : []
toString : []
getDate : [@java.lang.Deprecated]

まとめ

JavaのReflection APIでも同様の事が出来るらしいので、わざわざJavassistを使う必要はないと思います。 解析結果に基づいて元クラスにごにょごにょするようなユースケースがあればJavassistの出番ではないでしょうか。