クラスがあると想像してください:
@Something(someProperty = "some value")
public class Foobar {
//...
}
これはすでにコンパイルされており(ソースを制御できません)、jvmの起動時にクラスパスの一部です。実行時に「何らかの値」を別のものに変更できるようにしたいと考えています。これにより、その後のリフレクションはデフォルトの「何らかの値」ではなく新しい値になります。
これは可能ですか?もしそうなら、どのように?
クラスがあると想像してください:
@Something(someProperty = "some value")
public class Foobar {
//...
}
これはすでにコンパイルされており(ソースを制御できません)、jvmの起動時にクラスパスの一部です。実行時に「何らかの値」を別のものに変更できるようにしたいと考えています。これにより、その後のリフレクションはデフォルトの「何らかの値」ではなく新しい値になります。
これは可能ですか?もしそうなら、どのように?
このコードは、多かれ少なかれあなたが求めることを行います - それは概念の簡単な証明です:
declaredAnnotations出力:
oldAnnotation = 何らかの値
modifiedAnnotation = 別の値
public static void main(String[] args) throws Exception {
final Something oldAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + oldAnnotation.someProperty());
Annotation newAnnotation = new Something() {
@Override
public String someProperty() {
return "another value";
}
@Override
public Class<? extends Annotation> annotationType() {
return oldAnnotation.annotationType();
}
};
Field field = Class.class.getDeclaredField("annotations");
field.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) field.get(Foobar.class);
annotations.put(Something.class, newAnnotation);
Something modifiedAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("modifiedAnnotation = " + modifiedAnnotation.someProperty());
}
@Something(someProperty = "some value")
public static class Foobar {
}
@Retention(RetentionPolicy.RUNTIME)
@interface Something {
String someProperty();
}
これは Java 8 を搭載した私のマシンで動作します。ignoreUnknown注釈の値をtrue@JsonIgnoreProperties(ignoreUnknown = true)からfalseに変更します。
final List<Annotation> matchedAnnotation = Arrays.stream(SomeClass.class.getAnnotations()).filter(annotation -> annotation.annotationType().equals(JsonIgnoreProperties.class)).collect(Collectors.toList());
final Annotation modifiedAnnotation = new JsonIgnoreProperties() {
@Override public Class<? extends Annotation> annotationType() {
return matchedAnnotation.get(0).annotationType();
} @Override public String[] value() {
return new String[0];
} @Override public boolean ignoreUnknown() {
return false;
} @Override public boolean allowGetters() {
return false;
} @Override public boolean allowSetters() {
return false;
}
};
final Method method = Class.class.getDeclaredMethod("getDeclaredAnnotationMap", null);
method.setAccessible(true);
final Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) method.invoke(SomeClass.class, null);
annotations.put(JsonIgnoreProperties.class, modifiedAnnotation);
Java 8 でこのソリューションを試してください
public static void main(String[] args) throws Exception {
final Something oldAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + oldAnnotation.someProperty());
Annotation newAnnotation = new Something() {
@Override
public String someProperty() {
return "another value";
}
@Override
public Class<? extends Annotation> annotationType() {
return oldAnnotation.annotationType();
}
};
Method method = Class.class.getDeclaredMethod("annotationData", null);
method.setAccessible(true);
Object annotationData = method.invoke(getClass(), null);
Field declaredAnnotations = annotationData.getClass().getDeclaredField("declaredAnnotations");
declaredAnnotations.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) declaredAnnotations.get(annotationData);
annotations.put(Something.class, newAnnotation);
Something modifiedAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("modifiedAnnotation = " + modifiedAnnotation.someProperty());
}
@Something(someProperty = "some value")
public static class Foobar {
}
@Retention(RetentionPolicy.RUNTIME)
@interface Something {
String someProperty();
}
アノテーション属性値は定数である必要があります-したがって、深刻なバイトコード操作を実行したい場合を除いて、それは不可能です。必要なアノテーションを使用してラッパークラスを作成するなど、よりクリーンな方法はありますか?