Java进阶之注解

作者: 测试开发栈 | 来源:发表于2017-03-17 09:39 被阅读144次

    Java的注解应用广泛,且使代码结构看起来整洁明了,著名的JUnit和TestNG测试框架中就大量应用了注解。

    一、注解说明

    1、什么是注解?

    注解是在jdk 1.5开始提供的功能,目前被广泛使用。
    注解的描述: “Annotation其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行响应的处理。通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。“

    2、定义注解:

    一个自定义注解主要包含了三个部分:
    a.注解文件使用@interface来声明,格式:public @interface AnnotationName
    b.在注解外层有元注解声明(注解的注解),主要的作用就是对我们自定义注解的执行范围、时期进声明,一个自定义注解至少要声明@Target和@Retention元注解;
    C.注解内部可以定义变量和方法,其中方法名就是引用注解时的参数名,返回值类型就是参数的类型,通过default来设置方法的默认值。

    3、元注解:

    元注解的作用是负责注解其他注解。 Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target
    2.@Retention
    3.@Documented
    4.@Inherited
    这些类型和它们所支持的类java.lang.annotation包中可以找到。下面我们可以看一下每种元注解的作用和相应参数的使用。
    @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方):
    1.ElementType.CONSTRUCTOR : 用于描述构造器
    2.ElementType.FIELD : 用于描述属性
    3.ElementType.LOCAL_VARIABLE:用于描述局部变量
    4.ElementType.METHOD : 用于描述方法
    5.ElementType.PACKAGE : 用于描述包
    6.ElementType.PARAMETER : 用于描述参数
    7.ElementType.TYPE : 用于描述类、接口(包括注解类型) 或enum声明
    @Retention:表示需要在什么级别保存注解信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效):
    1.RetentionPoicy.SOURCE : 在源文件中有效(即源文件保留)
    2.RetentionPoicy.CLASS : 在class文件中有效(即class保留)
    3.RetentionPoicy.RUNTIME : 在运行时有效(即运行时保留)
    @ Documented:用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
    @Inherited:指示注释类型被自动继承。

    二、注解处理器

    注解处理器类库(Java.lang.reflect.AnnotatedElement):
      Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:
    Class:类定义 - Constructor:构造器定义
    Field:类的成员变量定义
    Method:类的方法定义
    Package:类的包定义
    java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
    AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个方法来访问Annotation信息:
    方法1: T getAnnotation(Class annotationClass): 返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
    方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
    方法3:boolean is AnnotationPresent(Class< ?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
    方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

    三、代码实例

    1、定义个Description注解:
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Description {
        int P1 = 1; //变量
        int P2 = 2;
        int P3 = 3;
        int P4 = 4;    
    
        String steps() default ""; //方法
    
        String expectation() default "";
    
        int priority();
    }
    
    2、注解解析类:
    public class AnnotationParser {
    
        private String className;
        private String methodName;
    
        public AnnotationParser(String className, String methodName){
            this.className = className;
            this.methodName = methodName;
        }
    
        public Description getDescription(){
            try {
                Class clazz = Class.forName(this.className);
                for(Method method : clazz.getDeclaredMethods()){
                    if(method.isAnnotationPresent(Description.class) && this.methodName.equals(method.getName())){
                        return method.getAnnotation(Description.class);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public String parser(String annotationName) {
            Description description = getDescription();
            if(description == null){
                return "";
            }
            switch (annotationName) {
            case "steps":
                return description.steps();
            case "expectation":
                return description.expectation();
            case "priority":
                return String.valueOf(description.priority());
            default:
                break;
            }    
            return "";
        }
    
    }
    
    3、测试类:
    public class AnnotationTest {
    
        @Description(steps="test1", expectation="print something", priority=Description.P1)
        public void test1(){
            System.out.println("test1.test");
        }
    
        @Description(steps="test2", expectation="print something by static method", priority=Description.P2)
        public static void test2(){
            System.out.println("test2.test");
        }
    
        public static void main(String[] args) {
            new AnnotationTest().test1();
    
            AnnotationParser aParser = new AnnotationParser("com.yag.demo.AnnotationTest","test1");
            System.out.println("steps=\"" + aParser.parser("steps") + "\", expectation=\"" + aParser.parser("expectation") + 
                    "\", priority=" + aParser.parser("priority"));
    
            test2();
    
            aParser = new AnnotationParser("com.yag.demo.AnnotationTest","test2");
            System.out.println("steps=\"" + aParser.parser("steps") + "\", expectation=\"" + aParser.parser("expectation") + 
                    "\", priority=" + aParser.parser("priority"));
        }
    }
    

    测试类执行效果:
    test1.test
    steps="test1", expectation="print something", priority=1
    test2.test
    steps="test2", expectation="print something by static method", priority=2

    PS: 更多原创技术好文和资料,请关注下方公众号:“测试开发栈”公众号是由具有多年测试、开发经验的老兵们共同管理和运营,旨在分享原创测试、开发相关技术,包括但不限于:测试方向:Web自动化测试、移动端自动化测试、Web服务端测试、接口测试等;开发方向:Java开发、Android开发、前端开发等;期望我们的经验和技术分享能让你每天都成长和进步,早日成为技术大牛~欢迎大家分享和转发我们的文章(分享转发请保留文章出处),以便让更多的朋友关注我们,同时也欢迎加入我们的QQ群交流和提问:427020613


    相关文章

      网友评论

        本文标题:Java进阶之注解

        本文链接:https://www.haomeiwen.com/subject/teujnttx.html