Skip to main content
 首页 » 编程设计

Java 在运行时修改注解参数值

2022年07月19日134zhujiabin

Java 在运行时修改注解参数值

注解是在java代码中增加一种元数据,这些元数据可以嵌入在class文件中在编译时处理,也可以保留至运行时通过Reflection进行访问。本文讨论如何在运行时修改注解值,我们示例使用类级别注解。

1. 注解

Java允许使用现有注解创建新的注解。最简单的注释形式是@符号后接注释名:

@Override 

下面创建自定义注解Greeter:

@Retention(RetentionPolicy.RUNTIME) 
public @interface Greeter {     
    public String greet() default "";  
} 

现在我们创建类Greeting并增加类级别注解:

@Greeter(greet="Good morning") 
public class Greetings {} 

现在可以使用反射访问注解,Java的Class提供方法getAnnotation获取类的注解:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class); 
System.out.println("Hello there, " + greetings.greet() + " !!"); 

2. 修改注解

Java Class类通过map管理注解:Annotation 类作为key,Annotation 对象作为值:

Map<Class<? extends Annotation>, Annotation> map; 

我们可以在运行时更新map在,在jdk7和jdk8中访问该map有差异。

2.1 jdk7 实现

Class类有annotations私有属性,为了访问该属性,需设置其可访问性为true。java提供getDeclaredField 方法通过名称访问属性。

Field annotations = Class.class.getDeclaredField(ANNOTATIONS); 
annotations.setAccessible(true); 

现在可以访问目标类注解map:

Map<Class<? extends Annotation>, Annotation> map = annotations.get(targetClass); 

该map包含所有注解及其值的信息。如果想修改Greeter注解值,可以通过更新注解对象:

map.put(targetAnnotation, targetValue); 

完整代码:

    private static final String ANNOTATIONS = "annotations"; 
 
    public static void alterAnnotationValueJDK7( 
        Class<?> targetClass, Class<? extends Annotation> targetAnnotation, Annotation targetValue) { 
        try { 
            Field annotations = Class.class.getDeclaredField(ANNOTATIONS); 
            annotations.setAccessible(true); 
 
            Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) annotations.get(targetClass); 
            System.out.println(map); 
            map.put(targetAnnotation, targetValue); 
            System.out.println(map); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 

2.2 jdk8实现

java8 通过AnnotationData类存储注解信息,我们可以通过annotationData方法访问该对象。同样也需要设置该方法的可访问性为true:

Method method = Class.class.getDeclaredMethod(ANNOTATION_METHOD, null); 
method.setAccessible(true); 

现在可以访问annotations字段,同样也需要设置该其访问属性:

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

通过annotations获取存储注解类和值的map,通过map可以修改注解值:

Map<Class<? extends Annotation>, Annotation> map = annotations.get(annotationData);  
map.put(targetAnnotation, targetValue); 

完整代码:

    private static final String ANNOTATION_METHOD = "annotationData"; 
    private static final String ANNOTATIONS = "annotations"; 
 
    public static void alterAnnotationValueJDK8( 
        Class<?> targetClass, Class<? extends Annotation> targetAnnotation, Annotation targetValue) { 
        try { 
            Method method = Class.class.getDeclaredMethod(ANNOTATION_METHOD, null); 
            method.setAccessible(true); 
 
            Object annotationData = method.invoke(targetClass); 
 
            Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS); 
            annotations.setAccessible(true); 
 
            Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData); 
            map.put(targetAnnotation, targetValue); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 

3. 完整测试

首先定义类DynamicGreeter:

public class DynamicGreeter implements Greeter { 
 
    private String greet; 
    public DynamicGreeter(String greet) { 
        this.greet = greet; 
    } 
 
    @Override 
    public Class<? extends Annotation> annotationType() { 
        return DynamicGreeter.class; 
    } 
 
    @Override 
    public String greet() { 
        return greet; 
    } 
} 

main函数中进行测试:

public static void main(String ...args) { 
    Greeter greetings = Greetings.class.getAnnotation(Greeter.class); 
    System.err.println("Hello there, " + greetings.greet() + " !!"); 
 
    Greeter targetValue = new DynamicGreeter("Good evening"); 
    alterAnnotationValueJDK8(Greetings.class, Greeter.class, targetValue); 
    //alterAnnotationValueJDK7(Greetings.class, Greeter.class, targetValue); 
 
    greetings = Greetings.class.getAnnotation(Greeter.class); 
    System.err.println("Hello there, " + greetings.greet() + " !!"); 
} 

初始时注解的值为“Good morning” ,接着现再创建一个值为“Good evening”的Greeter类对象。然后通过上面定义的方法修改注解值:

alterAnnotationValueJDK8(Greetings.class, Greeter.class, targetValue); 

运行结果:

Hello there, Good morning !! 
Hello there, Good evening !! 

成功在运行时修改了注解的值。

4. 总结

Java使用两个数据字段来存储注解数据:annotation、declaredAnnotations。两者之间的区别:第一个存储来自父类的注解,之后一个仅存储当前类的注解。由于JDK 7和JDK 8中实现getAnnotation有所不同,为了简单起见在这里使用annotations字段map。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/109276023
阅读延展