背景
あまりよくある話ではありませんが、@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() {
}