LoginSignup
4
11

More than 5 years have passed since last update.

Java8でAnnotationを動的に差し替えたい時

Last updated at Posted at 2017-05-24

背景

あまりよくある話ではありませんが、@Data(key="1")のようなアノテーションが設定されているクラスについて、動的に@Data(key="2")に変更したい場合があります。

解決策

Java8では、そのような場合の追加機能があるようで、以下のサイトを参考にしました。

もう少しシンプルに

public class DynamicAnnotationReplaceUtils {
    private static final String ANNOTATIONS = "annotations";
    private static final String ANNOTATION_DATA = "annotationData";

    /**
     * 特定のクラスのアノテーションを、別のアノテーションに置き換えます。
     *
     * @since
     *
     * @param targetClazz 置換対象のアノテーションを設定しているクラスです。
     * @param originalOne 置換元のアノテーションです。
     * @param newOne 置換先のアノテーションです。置換元のアノテーションをextednsしたクラスのインスタンスになる事が多いでしょう。
     */
  public static void annotationReplacedBy(Class<?> targetClazz, String originalName, Annotation newOne) {
        try {
            @SuppressWarnings("all")
            Method method = Class.class.getDeclaredMethod(ANNOTATION_DATA, null);
            method.setAccessible(true);

            Object annotationData = method.invoke(targetClazz);

            Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
            annotations.setAccessible(true);

            @SuppressWarnings("unchecked")
            Map<Class<? extends Annotation>, Annotation> map =
                    (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData);

            Annotation original = map.entrySet().stream().filter(e -> {
                return e.getKey().getSimpleName().equals(originalName());
            }).findFirst().get().getValue();

            if (original == null) {
                throw new IllegalArgumentException(
                        String.format("Class(%s) has not %s annotaion.",
                                targetClazz.getCanonicalName(), originalName));
            }

            map.put(original, newOne);
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex);
        }
    }

利用方法

DynamicAnnotationReplaceUtils.annotationReplacedBy(AnnotatedCalss.class, "Data", new ReplacedData()), 

置換先のアノテーションは?

置換元アノテーションの定義

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Data {
    public abstract String key();
}

置換先アノテーションの定義

public ReplacedData implements Data {
    public  String key() {
         return "2";
    }
}

置換元アノテーションが付与されたクラス

@Data(key = "1")
public TestData() {
}
4
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
11