• [03] 处理注解:反射



    1、AnnotatedElement接口

    如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。Java SE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。

    Java用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。如在java.lang.reflect包中有一个接口AnnotatedElement,其中定义了一些注解相关的方法,如判断某元素是否标注了注解的方法:
    • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
    • 这里的传参实际上就是传入 ”某个注解.class“ 

    另外,Java在java.lang.reflect 包下新增了 AnnotatedElement 接口:
    /** 
     * Represents an annotated element of the program currently running in this
     * VM.  This interface allows annotations to be read reflectively.  All
     * annotations returned by methods in this interface are immutable and
     * serializable.  It is permissible for the caller to modify the
     * arrays returned by accessors for array-valued enum members; it will
     * have no affect on the arrays returned to other callers.
     *
     * <p>If an annotation returned by a method in this interface contains
     * (directly or indirectly) a {@link Class}-valued member referring to
     * a class that is not accessible in this VM, attempting to read the class
     * by calling the relevant Class-returning method on the returned annotation
     * will result in a {@link TypeNotPresentException}.
     *
     * <p>Similarly, attempting to read an enum-valued member will result in
     * a {@link EnumConstantNotPresentException} if the enum constant in the
     * annotation is no longer present in the enum type.
     * 
     * <p>Finally, Attempting to read a member whose definition has evolved
     * incompatibly will result in a {@link
     * java.lang.annotation.AnnotationTypeMismatchException} or an
     * {@link java.lang.annotation.IncompleteAnnotationException}.
     *
     * @since 1.5
     * @author Josh Bloch
     */
    该接口代表程序中可以接受注解的程序元素,这句话怎么理解?我们从另一头倒着来理:

    (1)Field、Method、Constructor
    在反射中我们知道的像属性、方法、构造函数等封装的类,都继承了一个类,叫AccessibleObject;

    (2)AccessibleObject 参考链接
    java.lang.reflect包中有类AccessibleObject,从类的注释可以看出,The AccessibleObject class is the base class for Field, Method and Constructor objects. It provides the ability to flag a reflected object as suppressing default Java language access control checks when it is used. 显然,它可以让一个反射对象去禁止Java语言的访问控制检测,从而能够调用对象的私有属性和方法。

    这个类实现了一个接口,就是我们提到的AnnotatedElement了,实现该接口的类(Represents an annotated element of the program currently running in this VM.  This interface allows annotations to be read reflectively.)即代表那些可注解的元素,同时可以利用反射机制读取注解。所以,它的几个主要实现类有
    • Class  类
    • Constructor  构造器
    • Field  类的成员变量
    • Method  类的方法
    • Package  类的包

    注意:如Class、Package未继承AccessibleObject,毕竟这两个类不需要所谓的访问权限设置,但是它们是实现了AnnotatedElement接口的。

    所以当我们通过反射获取某个类的AnnotatedElement对象之后,就可以调用AnnotatedElement接口定义好的几个方法:

    • //判断该程序元素上是否包含指定类型的注解,存在返回true,否则返回false
    • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);

    • //返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null
    • <T extends Annotation> T getAnnotation(Class<T> annotationClass);

    • //返回该程序元素上存在的所有注解
    • Annotation[] getAnnotations();

    • //返回直接存在于该元素上的所有注解(与该接口中其他方法不同,该方法将忽略继承的注解)
    • //如果没有注解直接存在于此元素上,则返回长度为零的一个数组
    • //该方法调用者可以随意修改返回的数组,不会对其他调用者返回的数组产生影响
    • Annotation[] getDeclaredAnnotations();


    2、AnnotatedElement使用示例

    2.1 自定义注解

    /**
     * 水果名称注解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FruitName {
        String value() default "";
    }
    
    
    /**
     * 水果颜色注解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FruitColor {
        /**
         * 颜色枚举
         */
        public enum Color {BULE, RED, GREEN}
    
        /**
         * 颜色属性
         */
        public Color fruitColor() default Color.GREEN;
    }
    
    
    /**
     * 水果供应商注解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FruitProvider {
    
        /**
         * 供应商编号
         */
        public int id() default -1;
    
        /**
         * 供应商名称
         */
        public String name() default "";
    
        /**
         * 供应商地址
         */
        public String address() default "";
    
    }
    注意:当一个Annotation类型被定义为运行时(RUNTIME)的Annotation时,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

    2.2 使用自定义注解的类

    /**
     * 苹果类
     */
    public class Apple {
    
        /**
         * 苹果名称
         */
        @FruitName("红富士")
        private String appleName;
    
        /**
         * 苹果颜色
         */
        @FruitColor(fruitColor = FruitColor.Color.RED)
        private String appleColor;
    
        /**
         * 苹果供应商
         */
        @FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路89号红富士大厦")
        private String appleProvider;
    
        public String getAppleName() {
            return appleName;
        }
    
        public void setAppleName(String appleName) {
            this.appleName = appleName;
        }
    
        public String getAppleColor() {
            return appleColor;
        }
    
        public void setAppleColor(String appleColor) {
            this.appleColor = appleColor;
        }
    
        public String getAppleProvider() {
            return appleProvider;
        }
    
        public void setAppleProvider(String appleProvider) {
            this.appleProvider = appleProvider;
        }
    }

    2.3 注解处理

    public class FruitInfoUtil {
    
        /**
         * 获取水果信息
         * @param clazz 类对象
         */
        public static void getFruitInfo(Class<?> clazz) {
    
            String strFruitName;
            String strFruitColor;
            String strFruitProvider;
    
            //获取该类对象包含的所有属性对象
            Field[] fields = clazz.getDeclaredFields();
    
            //遍历属性对象
            for (Field field : fields) {
                //如果属性包含的注解是FruitName
                if (field.isAnnotationPresent(FruitName.class)) {
                    FruitName fruitName = field.getAnnotation(FruitName.class);
                    strFruitName = "水果名称:" + fruitName.value();
                    System.out.println(strFruitName);
                }
                //如果属性包含的注解是FruitColor
                else if (field.isAnnotationPresent(FruitColor.class)) {
                    FruitColor fruitColor = field.getAnnotation(FruitColor.class);
                    strFruitColor = "水果颜色:" + fruitColor.fruitColor().toString();
                    System.out.println(strFruitColor);
                }
                //如果属性包含的注解是FruitProvider
                else if (field.isAnnotationPresent(FruitProvider.class)) {
                    FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);
                    strFruitProvider = "供应商编号:" + fruitProvider.id()
                            + ";供应商名称:" + fruitProvider.name()
                            + ";供应商地址:" + fruitProvider.address();
                    System.out.println(strFruitProvider);
                }
            }
        }
        
    }

    2.4 测试和结果输出

    public class TestAnnotation {
        public static void main(String[] args) {
            FruitInfoUtil.getFruitInfo(Apple.class);
        }
    }
    
    //结果输出
    水果名称:红富士
    水果颜色:RED
    供应商编号:1;供应商名称:陕西红富士集团;供应商地址:陕西省西安市延安路89号红富士大厦


    3、参考链接

  • 相关阅读:
    eclipse快捷键
    Struts2框架(8)---Struts2的输入校验
    Struts2框架(5)---result结果集
    Spring框架(6)---AspectJ实现AOP
    Spring框架(4)---AOP讲解铺垫
    Spring框架(3)---IOC装配Bean(注解方式)
    Spring框架(2)---IOC装配Bean(xml配置方式)
    Spring框架(1)---Spring入门
    Mybatis框架(5)---动态sql
    Mybatis框架(4)---输入输出映射
  • 原文地址:https://www.cnblogs.com/deng-cc/p/7462588.html
Copyright © 2020-2023  润新知