java注解实现原理

一 什么是注解

    Annotation(注解)是JDK1.5版本引入的新特征。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。

它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)

进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,

并且供指定的工具或框架使用。

    注解插入代码是JVM自动去处理,JVM根据注解特性,自动处理创建文档,代码依赖,编译时检查等。

Annotation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

Annotation并不直接影响代码的语义,但是他可以被看做是程序的工具或者类库。它会反过来对正在运行的程序语义有所影响

Annotation可以从源文件、class文件或者在运行时通过反射机制多种方式被读取。

二 注解的作用

1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等。

2、跟踪代码依赖性,实现替代配置文件功能。比如Spring IOC依赖注入,可以使用大量注解配置,具有很大用处。

3、在编译时进行格式检查。如@Override 放在方法上,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出错误。


三 元注解

java.lang.annotation提供了四种元注解,专门注解其他的注解。

@Documented--注解是否将包含在JavaDoc中

@Retention--什么时候使用该注解

@Target--注解用于什么地方

@Inherited--是否允许子类继承该注解

1)@Retention--定义该注解的生命周期

@Retention源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

在@Retention源码中,定义了RetentionPolicy(保留策略),也即生命周期类型,源码:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

RetentionPolicy.SOURCE : 在编译阶段丢弃。

这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。

RetentionPolicy.CLASS : 在类加载的时候丢弃。

在字节码文件的处理中有用。注解默认使用这种方式。

RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。

我们自定义的注解通常使用这种方式。


2)@Target – 表示该注解用于什么地方。默认值为任何元素,表示该注解用于什么地方。

@Target源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

@Target中可用的ElementType源码:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

ElementType.TYPE: 用于描述类、接口(包括注解类型) 或enum声明。

ElementType.FIELD: 成员变量、对象、属性(包括enum实例)。

ElementType.METHOD: 用于描述方法。

ElementType.CONSTRUCTOR: 用于描述构造器。

ElementType.LOCAL_VARIABLE: 用于描述局部变量。

ElementType.ANNOTATION_TYPE: 用于描述注解类型。

ElementType.PACKAGE: 用于描述包。

ElementType.PARAMETER: 用于描述参数。

3) @Documented--一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。

@Documented源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

4) @Inherited--定义该注释和子类的关系。

@Inherited源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。

如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

四 内置注解

1)@Override

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

java.lang.Override是一个标记类型注解,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。

如果我们使用了这种注解在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。

2)@Deprecated

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

Deprecated也是一种标记类型注解。当一个类型或者类型成员使用@Deprecated修饰的话,

编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的“延续性”:

如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员

并不是被声明为@Deprecated,但编译器仍然要报警。

3)@SuppressWarnings

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

SuppressWarning不是一个标记类型注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。

对于javac编译器来讲,被-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。

4)@FunctionalInterface

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

java8中函数式编程接口定义时使用的注解,类似Override注解作用,如果使用该注解,定义的不是一个函数式编程接口,

编译器会报编译错误来警示。

五 自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。

在定义注解时,不能继承其他的注解或接口。

1)自定义最简单注解

package com.lanhuigu.annotation;

public @interface MyAnnotation {

}

注解使用:

package com.lanhuigu.annotation;

public class AnnotationTest {
    @MyAnnotation
    public void execute() {
        System.out.println("method");
    }
}

2)添加变量

package com.lanhuigu.annotation;

public @interface MyAnnotation {
    String value1();
}

注解使用:

package com.lanhuigu.annotation;

public class AnnotationTest {
    @MyAnnotation(value1 = "添加变量")
    public void execute() {
        System.out.println("method");
    }
}

当注解中使用的属性名为value时,对其赋值时可以不指定属性的名称而直接写上属性值接口;

除了value意外的变量名都需要使用name=value的方式赋值。也即,当这里定义属性名时,value1修改为value,

使用时就不用显示指明value1="添加变量",而是直接写为@MyAnnotation("添加变量")。

3)添加默认值

package com.lanhuigu.annotation;

public @interface MyAnnotation {
    String value1() default "hello";
}
4)多变量使用枚举
package com.lanhuigu.annotation;

public @interface MyAnnotation {
    String value1() default "hello";
    MyEnum value2() default MyEnum.Sunny;
}
enum MyEnum{
    Sunny,Rainy
}

注解使用:

package com.lanhuigu.annotation;

public class AnnotationTest {
    @MyAnnotation(value1 = "添加变量", value2 = MyEnum.Sunny)
    public void execute() {
        System.out.println("method");
    }
}

5)添加数组变量

package com.lanhuigu.annotation;

public @interface MyAnnotation {
    String value1() default "hello";
    MyEnum value2() default MyEnum.Sunny;
    String[] value3() default "hello Array";
}
enum MyEnum{
    Sunny,Rainy
}

注解使用:

package com.lanhuigu.annotation;

public class AnnotationTest {
    @MyAnnotation(value1 = "添加变量", value2 = MyEnum.Sunny, value3 = {"a", "b"})
    public void execute() {
        System.out.println("method");
    }
}

6)添加元注解到自定义注解,关于元注解参考上面元注解说明

package com.lanhuigu.annotation;

import java.lang.annotation.*;

@Documented // 说明该注解将被包含在javadoc中
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target(ElementType.METHOD) // 定义注解的作用目标**作用范围字段、枚举的常量/方法
public @interface MyAnnotation {
    String value1() default "hello";
    MyEnum value2() default MyEnum.Sunny;
    String[] value3() default "hello Array";
}
enum MyEnum{
    Sunny,Rainy
}

7)通过反射读取注解中的信息

package com.lanhuigu.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        AnnotationTest annotationTest = new AnnotationTest();
        // 获取AnnotationTest的Class实例
        Class<AnnotationTest> c = AnnotationTest.class;
        // 获取需要处理的方法Method实例
        Method method = c.getMethod("execute", new Class[]{});
        // 判断该方法是否包含MyAnnotation注解
        if(method.isAnnotationPresent(MyAnnotation.class)){
            // 获取该方法的MyAnnotation注解实例
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
            // 执行该方法
            method.invoke(annotationTest, new Object[]{});
            // 获取myAnnotation中value3变量,然后打印数组中元素
            String[] value3 = myAnnotation.value3();
            for (String v : value3) {
                System.out.println(v);
            }
        }
        // 获取方法上的所有注解
        Annotation[] annotations = method.getAnnotations();
        for(Annotation annotation : annotations){
            System.out.println(annotation);
        }
    }
}


猜你喜欢

转载自blog.csdn.net/yhl_jxy/article/details/80826238