深入理解java注解及应用
Posted 小妖云汐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解java注解及应用相关的知识,希望对你有一定的参考价值。
深入理解java注解及应用
注解的本质
Java注解(Annotation)又称 Java 标注,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 ,也支持自定义 Java 标注。
- 元注解:用于修饰注解的注解,通常用在注解的定义上。
- java.lang.annotation包提供了四种元注解:
元注解 底层是什么?
元注解的处理逻辑由apt tool提供,对注解的行为做出一些限制,例如生命周期,作用范围等。
- 元注解中的参数
注解中的参数,通过源码中的注释都有明确的说明,这里举例一下@Retention接口的参数,其他几个都是类似的步骤可以查看
进入RetentionPolicy类中可以查看到,参数的值有哪些以及每个参数的含义
Java内置注解
@Override
作用:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
@Target(ElementType.METHOD)]:该注解用于描述方法
@Retention(RetentionPolicy.SOURCE):该注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
@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
作用:指示编译器去忽略注解中声明的警告
@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的生效方式
通过在源码中打断点的方式,在org.springframework.context.annotation.ConfigurationClassParser类的doProcessConfigurationClass中,检索@Bean修饰的方法,把方法的metadata和类configClass封装成BeanMethod加入到ConfigurationClass中的beanMethods集合中,也就是收集@Bean修饰的方法。
进入retrieveBeanMethodMetadata中,可以看到也是通过反射的方式获取到被@Bean注解修饰的数据。
然后,具体的逻辑就不做过多解释啦,大家打个断点自己跟着走一走看一看是最好的,本篇文章实际上就是想解释注解到底是个怎么回事,虽然标题是深入理解,感觉其实写的也没有那么深入啦!
以上是关于深入理解java注解及应用的主要内容,如果未能解决你的问题,请参考以下文章