深入理解java注解及应用

Posted 小妖云汐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解java注解及应用相关的知识,希望对你有一定的参考价值。

注解的本质

Java注解(Annotation)又称 Java 标注,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 ,也支持自定义 Java 标注。

  • 元注解:用于修饰注解的注解,通常用在注解的定义上。
  • java.lang.annotation包提供了四种元注解:
    在这里插入图片描述

元注解 底层是什么?
元注解的处理逻辑由apt tool提供,对注解的行为做出一些限制,例如生命周期,作用范围等。

  • 元注解中的参数
    注解中的参数,通过源码中的注释都有明确的说明,这里举例一下@Retention接口的参数,其他几个都是类似的步骤可以查看
    @Retention注解
    进入RetentionPolicy类中可以查看到,参数的值有哪些以及每个参数的含义
    值的查看

Java内置注解

@Override

作用:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
override
@Target(ElementType.METHOD)]:该注解用于描述方法
@Retention(RetentionPolicy.SOURCE):该注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃

@Deprecated

作用:标记过时方法。如果使用该方法,会报编译警告
deprecated
@Documented:注解是否将包含在JavaDoc中
@Retention(RetentionPolicy.RUNTIME):注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
  - CONSTRUCTOR:可用于描述构造器
  - FIELD:可用于描述域
  - LOCAL_VARIABLE:可用于描述局部变量
  - METHOD:可用于描述方法
  - PACKAGE:可用于描述包
  - PARAMETER:可用于描述参数
  - TYPE:可用于描述类、接口或enum声明

@SuppressWarnings

作用:指示编译器去忽略注解中声明的警告
SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}):
  - FIELD:可用于描述域
  - METHOD:可用于描述方法
  - PARAMETER:可用于描述参数
  - CONSTRUCTOR:可用于描述构造器
  - LOCAL_VARIABLE:可用于描述局部变量

@Retention(RetentionPolicy.SOURCE):注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃

自定义注解

如何自定义一个注解?

/******************自定义注解-MyClass-修饰类******************/
@Target(ElementType.TYPE) //用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //jvm加载class文件之后,仍然存在
@Inherited //具有继承性
@interface MyClass
{
    String value();
}
/******************自定义注解-MyMethod-修饰方法******************/
@Documented //包含在JavaDoc中
@Target({ElementType.METHOD}) //用于描述方法
@Retention(RetentionPolicy.RUNTIME) //jvm加载class文件之后,仍然存在
@interface MyMethod
{
}
/******************自定义注解-MyVar-修饰变量******************/
@Target(ElementType.FIELD) //用于描述变量
@Retention(RetentionPolicy.RUNTIME) //jvm加载class文件之后,仍然存在
@interface MyVar
{
    int min(); //最小长度
}

如何使用自定义注解?

@MyClass("Test")
class Test{
    @MyVar(min = 5) //指定name的最小长度为5
    private String name;

	public Test(String name) {
        this.name = name;
    }

    @MyMethod
    public void printName() {
        System.out.println("name is " + name);
    }
}

自定义注解如何生效?

只是定义一个注解,并把注解使用在合适的位置,但是注解本身并没有生效。一个注解,如果没有解析它的代码,它就只是一个没有意义的标记。

public class AnnotationTest {
    public static void main(String[] args) {
        Test test = new Test("cloverning");
        try {
            Class clazz = test.getClass();
            //根据反射判断对象是否包含MyClass注解
            if (clazz.isAnnotationPresent(MyClass.class)) {
                MyClass myClazz = (MyClass) clazz.getAnnotation(MyClass.class);
                System.out.println("MyClass:" + myClazz.value());
                //获取Test类下的属性
                Field field = clazz.getDeclaredField("name");
                if (field.isAnnotationPresent(MyVar.class)) { //该属性是否包含MyVar注解
                    MyVar myVar = field.getDeclaredAnnotation(MyVar.class);
                    field.setAccessible(true);
                    //效验该类中被MyVar修饰的变量是否符合规则
                    //String.valueOf(field.get(test)):获取test中name的值
                    //myVar.min():获取注解中设置的变量的最小长度min
                    if (String.valueOf(field.get(test)).length() >= myVar.min()) {
                        //符合规则,则执行被MyMethod注解修饰的方法
                        Method[] myMethods = clazz.getDeclaredMethods();
                        for (Method method : myMethods) {
                            if (method.isAnnotationPresent(MyMethod.class)) {
                                method.invoke(test);
                            }
                        }
                    } else {
                        System.out.println("name长度不符合规则,值为:" + field.get(test));
                    }
                } else {
                    System.out.println("name属性没有配置MyVar注解!");
                }
            } else {
                System.out.println("没有配置MyClass注解!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注解其实就是一个实现了Annotation的接口,而我们通过反射获取到的实际上是通过JDK动态代理生成的代理类,这个类实现了我们的注解接口。在实际项目中,使注解生效的代码一般通过拦截器或者切面来实现

Spring中@Bean注解

@Bean的定义

bean

@Bean的使用

beanuse

@Bean的生效方式

通过在源码中打断点的方式,在org.springframework.context.annotation.ConfigurationClassParser类的doProcessConfigurationClass中,检索@Bean修饰的方法,把方法的metadata和类configClass封装成BeanMethod加入到ConfigurationClass中的beanMethods集合中,也就是收集@Bean修饰的方法。
在这里插入图片描述
进入retrieveBeanMethodMetadata中,可以看到也是通过反射的方式获取到被@Bean注解修饰的数据。
在这里插入图片描述
然后,具体的逻辑就不做过多解释啦,大家打个断点自己跟着走一走看一看是最好的,本篇文章实际上就是想解释注解到底是个怎么回事,虽然标题是深入理解,感觉其实写的也没有那么深入啦!

以上是关于深入理解java注解及应用的主要内容,如果未能解决你的问题,请参考以下文章

深入理解java注解及应用

深入理解java 注解(Annotation)重版

深入理解Java:注解(Annotation)基本概念

深入理解Java:注解(Annotation)基本概念

深入理解Java:注解(Annotation)基本概念(转)

深入理解Java注解类型