前言
在学习spring注解驱动开发之前,我们先来回顾一下Java的注解相关的知识。
一、注解的定义
直接看代码:
/*自定义注解@Book*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface Book {
String name() default "";
String author() ;
}
/*使用@Book注解标示该类*/
@Book(author = "Lucy")
public class BookSpring {
}
/*获取类上的注解,提取类上的注解有两种方式:1、asm字节码库 2、通过Class对象*/
public class AnnotationTest {
@Test
public void test1(){
try {
//方式一:通过Class对象,可以获取到类上的注解,因为Class实现了AnnotatedElement接口
Class clazz = Class.forName("com.xinchao.test.annotation.BookSpring");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation: annotations) {
System.out.println(annotation.getClass().getName());
if(annotation instanceof Book){
Book book = (Book) annotation;
System.out.println(book.name()+book.author());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
上面简单的演示了一下自定义注解以及使用,再到提取类上的注解。接下来详细讲解如何自定义注解
1.自定义注解
第一步:首先使用 @interface标明该类是一个注解类
第二步:使用元注解修饰自定义的注解。元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种,下面我会详细介绍这五个修饰注解的注解的作用。
第三步:定义属性,String name() default “”; 在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。方法名代表属性的名称,只能是空参数。default是可选的默认值,如果不设置,那么使用的时候必须传。也就是没有default修饰,使用的时候不传会报错。
注意:如果属性名为value的时候,在使用时,如果只有value参数,那么vaue可以省略。示例:
@Table("student")
public class Student {
}
或者
@Table(value="student")
public class Student {
}
元注解:
@Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
@Documented
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。
@Target
Target 是目标的意思,@Target 指定了注解运用的地方。
你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
注意:该注解只能继承类,如果是实现的接口则没有效果。
@Repeatable
Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。
什么样的注解会多次应用呢?通常是注解的值可以同时取多个。
2.注解的本质
「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』。
The common interface extended by all annotation types
所有的注解类型都继承自这个普通的接口(Annotation)
3.注解的提取
注解的提取主要有两种方式
- 通过Class对象获取,因为Class实现了AnnotatedElement接口
- ASM读取class字节码获取类的注解信息,Spring用的此种方式,优点就是不需要加载该类。
public interface AnnotatedElement {
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) //是否存在某个类型的注解
<T extends Annotation> T getAnnotation(Class<T> annotationClass);//获取指定的注解
Annotation[] getAnnotations();//获取所有的注解
<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) ;
<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass);
Annotation[] getDeclaredAnnotations();//获取该类的注解不包含集成来的
}
二、Spring注解
基于Java的注解,Spring对注解功能进行了增强,其中包括组合注解,别名@AliasFor等,用于处理注解有一个工具类:AnnotatedElementUtils和AnnotationUtils。
举个例子,比如@Configuration注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/*代表Configuration的value属性会被映射到Component注解的属性*/
@AliasFor( annotation = Component.class)
String value() default "";
}