一、注解:注解的本质就是一个继承了 Annotation 接口的接口
二、JAVA 中有以下几个『元注解』:
@Target: 注解的作用目标
@Retention:注解的生命周期
@Documented:注解是否应当被包含在 JavaDoc 文档中
@Inherited:是否允许子类继承该注解
1.其中,@Target 用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的
例如:@Target(value = {ElementType.FIELD})
ElementType.TYPE: 允许被修饰的注解作用在类、接口和枚举上
ElementType.FIELD: 允许作用在属性字段上
ElementType.METHOD: 允许作用在方法上
ElementType.PARAMETER: 允许作用在方法参数上
ElementType.CONSTRUCTOR: 允许作用在构造器上
ElementType.LOCAL_VARIABLE: 允许作用在本地局部变量上
ElementType.ANNOTATION_TYPE: 允许作用在注解上
ElementType.PACKAGE: 允许作用在包上
2.@Retention 用于指明当前注解的生命周期,用法如下
例如:@Retention(value = RetentionPolicy.RUNTIME
RetentionPolicy.SOURCE: 当前注解编译期可见,不会写入 class 文件
RetentionPolicy.CLASS: 类加载阶段丢弃,会写入 class 文件
RetentionPolicy.RUNTIME: 永久保存,可以反射获取
@Retention 注解指定了被修饰的注解的生命周期,一种是只能在编译期可见,编译后会被丢弃,一种会被编译器编译进 class 文件中,无论是类或是方法,乃至字段,他们都是有属性表的,而 JAVA
虚拟机也定义了几种注解属性表用于存储注解信息,但是这种可见性不能带到方法区,类加载时会予以丢弃,最后一种则是永久存在的可见性
简单解析:springboot的启动注解
@Target(ElementType.TYPE) //允许被修饰的注解作用在类、接口和枚举上
@Retention(RetentionPolicy.RUNTIME) //永久保存,可以反射获取
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
....
}
三、注解与反射
1.对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。
getAnnotation:返回指定的注解
isAnnotationPresent:判定当前元素是否被指定注解修饰
getAnnotations:返回所有的注解
getDeclaredAnnotation:返回本元素的指定注解
getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的
2.注解本质上是继承了 Annotation 接口的接口,而当你通过反射,也就是我们这里的 getAnnotation 方法去获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解(接口)的代理类 代理类实现接口 Hello 并重写其所有方法,包括 value 方法以及接口 Hello 从 Annotation 接口继承而来的方法
案例:
1.自定义一个注解类型Hello
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = {ElementType.FIELD,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Hello {
String value();
}
2.使用注解类型
import com.gsau.project01.Annotation.Hello;
import java.lang.reflect.Method;
public class test {
@Hello("HelloWorld!!!")
public static void main(String[] args) throws NoSuchMethodException {
Class cls = test.class;
Method method = cls.getMethod("main", String[].class);
Hello hello = method.getAnnotation(Hello.class);
System.out.println(hello.value());
}
}
3.执行结果://打印----->HelloWorld!!!
3.最后再总结一下案例反射注解的工作过程:
1.首先,我们通过键值对的形式可以为注解属性赋值,像这样:@Hello("HelloWorld!!!")。
2.接着,你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,最后会将注解信息写入元素的属性表。
3.然后,当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。
4.最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。那么这样,一个注解的实例就创建出来了,它本质上就是一个代理类,应当去理解好 AnnotationInvocationHandler 中 invoke
方法的实现逻辑,这是核心。一句话概括就是,通过方法名返回注解属性值