zoukankan      html  css  js  c++  java
  • 注解基础篇:自定义Java Annotation

    写在前面


    JDK5增加了对Annotation(注解)的支持,Annotation是代码里的特殊标记,这些标记可以在编译,类加载和运行时被读取读取出来,并执行相应的处理和操作!比如在不改变程序逻辑的情况下,开发人员可以在代码中嵌入一些补充信息,代码分析和开发部署工具APT(AnnotationProcessTool)可以通过这些信息进行验证或部署。

    APT工具在处理注解时,会根据源文件的Annotation信息,生成附加源文件或者进行一些其他操作!最后在车鞥徐编译时期,APT会将生成的附加源文件和原有源文件进行合并,生成最终的*.class文件!默认情况下,Annotation可以修饰任何程序元素(ElementType),包括类,接口,成员方法,成员变量。根据Annotation是否带成员变量,可分为两类:

    • 标记Annotation,根据自身存在与否提供信息;
    • 元数据Annotation,根据接收的成员变量值,提供元数据信息;

    设计Annotation


    与定义一个接口很相似,自定义Annotation使用@interface关键字:

    //字符定义Annotation
    public @interface CustomAnno {
    }
    
    //使用上面自定义的Annotaiton,修饰类
    @CustomAnno
    public class BeautifulClass {
        //修饰成员方法
        @CustomAnno
        public void toPlus() {...}
    }
    

    Annotation也可以有成员变量,如下定义:

    public @interface HelloAnno {
        //定义带两个成员变量的Annotation
        //Annotaion中的成员变量以方法的形式来定义
        String apple();
        Integer pear();
    }
    
    //使用带成员变量的Annotation
    public class beautifulClass2 {
        //需要为成员变量赋值
        @HelloAnno(apple="apple", pear=101)
        public void toPlus() {...}
    }
    

    当然,带成员变量的Annotation也可以为期指定初始值(默认值),在哪在使用注解的时候就不需要再次未改成员变量赋值了;当然,如果需要的话也可以再次赋值,则新值会覆盖默认值!如下:

    public @interface BlackAnno {
        //定义带两个成员变量的Annotation
        //Annotaion中的成员变量以方法的形式来定义
        //指定成员变量的默认值,使用default关键字
        String apple() default "black";
        Integer pear() default 101;
    }
    
    //使用带成员变量的Annotation
    public class beautifulClass3 {
        //@HelloAnno已经指定了默认值
        //则使用时不需要赋值,直接使用即可
        @HelloAnno
        public void toPlus1() {...}
        
        //如果需要,也可以为成员变量赋值,则会覆盖默认值
        @HelloAnno(apple="apple", pear=101)
        public void toPlus2() {...}
    }
    

    提取Annotation


    使用Annotation修饰了类以及成员方法后,这些Annotation不会自己生效,必须有开发者提供相应的工具来提取并处理Annotation信息。

    java.lang.reflect包下包含一些实现反射功能的工具类:比如定义Annotation时使用@Rentation(RetentionPolicy.RUNTIME)修饰,则Annotation会被编译器保存在*.class文件中,Java在运行时会通过反射读取类文件中的Annotation!

    AnnotatedElement接口是所有程序元素(Class,Method,Constructor等)的父接口,所以获得某个AnnotationElement对象后,就可以调用该对象的如下方法来访问Annotation注解信息:

    • <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    • Annotation[] getAnnotations()
    • default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
    • default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
    • Annotation[] getDeclaredAnnotations()
    • default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)
    • default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

    自定义注解demo


    自定义一个Java注解 @CustomAnno,代码如下:

    /**
     * 自定义一个 Java 注解
     *  1. @Retention(RetentionPolicy.RUNTIME)
     *      运行时读取并处理注解信息
     *  2. @Target(ElementType.METHOD)
     *      自定义注解只能修饰方法
     *
     * Created by wondertwo on 2016/10/16.
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface CustomAnno {}
    

    用以上自定义的注解,来修饰Person类的八个方法,代码如下:

    /**
     * Created by wondertwo on 2016/10/16.
     */
    public class Person {
        /**
         * 使用自定义注解 @CustomAnno 修饰以下 8个方法
         */
        @CustomAnno
        public static void method_1() {}
        @CustomAnno
        public static void method_2() {}
        @CustomAnno
        public static void method_3() {}
        @CustomAnno
        public static void method_4() {}
        @CustomAnno
        public static void method_5() {}
        @CustomAnno
        public static void method_6() {}
        @CustomAnno
        public static void method_7() {}
        @CustomAnno
        public static void method_8() {}
    }
    

    测试注解,通过反射调用方法,代码如下:

    /**
     * Created by wondertwo on 2016/10/16.
     */
    public class AnnoTest {
        public static void main(String[] args) throws ClassNotFoundException {
            process("me.wondertwo.annotation.Person"); //Person
        }
        private static void process(String clazz) throws ClassNotFoundException {
            int passed= 0 , failed = 0;
            //遍历clazz对应类的所有方法
            for (Method method : Class.forName(clazz).getMethods()) {
                //如果该方法使用了自定义注解 @CustomAnno 修饰
                if (method.isAnnotationPresent(CustomAnno.class)) {
                    try {
                        method.invoke(null);
                        passed++;
                    } catch (Exception e) {
                        e.printStackTrace();
                        System.out.println("方法 " + method + " 测试失败,出现异常");
                        failed++;
                    }
                }
            }
            System.out.println("一共运行了 " + (passed+failed) + " 个方法");
            System.out.println("成功运行方法 " + passed + " 个");
            System.out.println("失败运行方法 " + failed + " 个");
        }
    }
    /**
     * 运行结果输出如下:
     * 一共运行了 8 个方法
     * 成功运行方法 8 个
     * 失败运行方法 0 个
     * Process finished with exit code 0
     */
    

    我们的自定义注解,成功的被提取出来并执行了!

  • 相关阅读:
    LeetCode 81 Search in Rotated Sorted Array II(循环有序数组中的查找问题)
    LeetCode 80 Remove Duplicates from Sorted Array II(移除数组中出现两次以上的元素)
    LeetCode 79 Word Search(单词查找)
    LeetCode 78 Subsets (所有子集)
    LeetCode 77 Combinations(排列组合)
    LeetCode 50 Pow(x, n) (实现幂运算)
    LeetCode 49 Group Anagrams(字符串分组)
    LeetCode 48 Rotate Image(2D图像旋转问题)
    LeetCode 47 Permutations II(全排列)
    LeetCode 46 Permutations(全排列问题)
  • 原文地址:https://www.cnblogs.com/wondertwo/p/6017409.html
Copyright © 2011-2022 走看看