是否有可能注释在运行时添加一个对象(对我来说特别的一种方法)?
为了更说明一下:我有两个模块,moduleA和moduleB。 moduleB取决于moduleA,它不依赖于任何东西。 (MODA是我的核心数据类型和接口和这样,MODB是分贝/数据层)MODB也取决于externalLibrary。 在我的情况下,商业方法是从MODA移交一类externalLibrary,这需要一定的方法来进行注释。 具体的注解是externalLib的一部分,正如我所说,MODA不依赖于externalLib,我想保持这种方式。
所以,这是可能的,或者你有看这个问题的其他方面的建议?
这是不可能在运行时添加注释,这听起来像你需要引入一个适配器,该模块B使用包从模块A暴露所需的注释方法的对象。
这是通过字节码工具库可能如Javassist进行 。
特别是,看看AnnotationsAttribute类关于如何创建/组注解和示例字节码API教程部分 ,了解如何处理类文件一般准则。
这是什么,但简单明了,虽然 - 我不推荐这种方法,建议你认为汤姆的回答不是,除非你需要为类数量庞大做到这一点(或者说类不提供给你,直到运行时,因此写一个适配器是不可能的)。
也可以在运行时使用Java反射API的注释添加到Java类。 基本上由一种必须重新创建在该类中定义的内部注释地图java.lang.Class
(在内部类中定义或为Java 8 java.lang.Class.AnnotationData
)。 当然这种做法是相当哈克,并可能随时更新的Java版本打破。 但是对于快速和肮脏的测试/原型这种方法有时可能有用。
坡口的Java 8概念例子:
public final class RuntimeAnnotations {
private static final Constructor<?> AnnotationInvocationHandler_constructor;
private static final Constructor<?> AnnotationData_constructor;
private static final Method Class_annotationData;
private static final Field Class_classRedefinedCount;
private static final Field AnnotationData_annotations;
private static final Field AnnotationData_declaredAnotations;
private static final Method Atomic_casAnnotationData;
private static final Class<?> Atomic_class;
static{
// static initialization of necessary reflection Objects
try {
Class<?> AnnotationInvocationHandler_class = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
AnnotationInvocationHandler_constructor = AnnotationInvocationHandler_class.getDeclaredConstructor(new Class[]{Class.class, Map.class});
AnnotationInvocationHandler_constructor.setAccessible(true);
Atomic_class = Class.forName("java.lang.Class$Atomic");
Class<?> AnnotationData_class = Class.forName("java.lang.Class$AnnotationData");
AnnotationData_constructor = AnnotationData_class.getDeclaredConstructor(new Class[]{Map.class, Map.class, int.class});
AnnotationData_constructor.setAccessible(true);
Class_annotationData = Class.class.getDeclaredMethod("annotationData");
Class_annotationData.setAccessible(true);
Class_classRedefinedCount= Class.class.getDeclaredField("classRedefinedCount");
Class_classRedefinedCount.setAccessible(true);
AnnotationData_annotations = AnnotationData_class.getDeclaredField("annotations");
AnnotationData_annotations.setAccessible(true);
AnnotationData_declaredAnotations = AnnotationData_class.getDeclaredField("declaredAnnotations");
AnnotationData_declaredAnotations.setAccessible(true);
Atomic_casAnnotationData = Atomic_class.getDeclaredMethod("casAnnotationData", Class.class, AnnotationData_class, AnnotationData_class);
Atomic_casAnnotationData.setAccessible(true);
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, Map<String, Object> valuesMap){
putAnnotation(c, annotationClass, annotationForMap(annotationClass, valuesMap));
}
public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, T annotation){
try {
while (true) { // retry loop
int classRedefinedCount = Class_classRedefinedCount.getInt(c);
Object /*AnnotationData*/ annotationData = Class_annotationData.invoke(c);
// null or stale annotationData -> optimistically create new instance
Object newAnnotationData = createAnnotationData(c, annotationData, annotationClass, annotation, classRedefinedCount);
// try to install it
if ((boolean) Atomic_casAnnotationData.invoke(Atomic_class, c, annotationData, newAnnotationData)) {
// successfully installed new AnnotationData
break;
}
}
} catch(IllegalArgumentException | IllegalAccessException | InvocationTargetException | InstantiationException e){
throw new IllegalStateException(e);
}
}
@SuppressWarnings("unchecked")
private static <T extends Annotation> Object /*AnnotationData*/ createAnnotationData(Class<?> c, Object /*AnnotationData*/ annotationData, Class<T> annotationClass, T annotation, int classRedefinedCount) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) AnnotationData_annotations.get(annotationData);
Map<Class<? extends Annotation>, Annotation> declaredAnnotations= (Map<Class<? extends Annotation>, Annotation>) AnnotationData_declaredAnotations.get(annotationData);
Map<Class<? extends Annotation>, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(annotations);
newDeclaredAnnotations.put(annotationClass, annotation);
Map<Class<? extends Annotation>, Annotation> newAnnotations ;
if (declaredAnnotations == annotations) {
newAnnotations = newDeclaredAnnotations;
} else{
newAnnotations = new LinkedHashMap<>(annotations);
newAnnotations.put(annotationClass, annotation);
}
return AnnotationData_constructor.newInstance(newAnnotations, newDeclaredAnnotations, classRedefinedCount);
}
@SuppressWarnings("unchecked")
public static <T extends Annotation> T annotationForMap(final Class<T> annotationClass, final Map<String, Object> valuesMap){
return (T)AccessController.doPrivileged(new PrivilegedAction<Annotation>(){
public Annotation run(){
InvocationHandler handler;
try {
handler = (InvocationHandler) AnnotationInvocationHandler_constructor.newInstance(annotationClass,new HashMap<>(valuesMap));
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
return (Annotation)Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class[] { annotationClass }, handler);
}
});
}
}
用法示例:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestAnnotation {
String value();
}
public static class TestClass{}
public static void main(String[] args) {
TestAnnotation annotation = TestClass.class.getAnnotation(TestAnnotation.class);
System.out.println("TestClass annotation before:" + annotation);
Map<String, Object> valuesMap = new HashMap<>();
valuesMap.put("value", "some String");
RuntimeAnnotations.putAnnotation(TestClass.class, TestAnnotation.class, valuesMap);
annotation = TestClass.class.getAnnotation(TestAnnotation.class);
System.out.println("TestClass annotation after:" + annotation);
}
输出:
TestClass annotation before:null
TestClass annotation after:@RuntimeAnnotations$TestAnnotation(value=some String)
这种方法的局限性:
- Java的新版本可能会在任何时候打破代码。
- 上面的例子只适用于Java的8 - 使其成为较老的Java版本的工作将需要检查在运行时的Java版本,并相应地改变实施。
- 如果注解的类被重新定义 (例如,在调试过程中),注释将丢失。
- 不彻底的测试; 不知道是否有任何不良的副作用- 使用您自己的风险 ...
有可能通过在运行时创建的注释代理 。 然后,您可以通过反射将它们添加到您的Java对象在其他的答案建议(但你可能会更好找到另一种方式来处理,如通过反射与现有各类搞乱可能是危险的,很难调试)。
但它是不是很容易......我写了一个库调用,希望适当, Javanna只是要做到这一点很容易地用干净的API。
这是一个在JCenter和Maven的中央 。
使用它:
@Retention( RetentionPolicy.RUNTIME )
@interface Simple {
String value();
}
Simple simple = Javanna.createAnnotation( Simple.class,
new HashMap<String, Object>() {{
put( "value", "the-simple-one" );
}} );
如果地图标注不符的任何条目声明字段(S)和类型,则将引发异常。 如果缺少没有默认值的任何值,则抛出异常。
这使得可以假设创建成功是安全的,因为编译时注释实例使用每一个注释实例。
作为奖励,这LIB也可以解析注解类,并返回注释作为地图的值:
Map<String, Object> values = Javanna.getAnnotationValues( annotation );
这是很方便的创建迷你框架。