第3章
Spring Boot的相关注解
对第1章中例1-1的代码进行分析,可以知道注解(Annotation)在Spring Boot开发中占有重要的地位。注解为在代码中添加信息提供了一种形式化的方法,通过注解可以很方便地在代码中某个地方使用被注解的对象。注解常见的作用包括生成文档、跟踪代码依赖性和替代配置文件、在编译时进行格式检查(如@Override 放在方法前)等。
为了帮助读者更好地理解后面章节的示例代码,本章介绍Spring Boot注解以及和Spring Boot注解密切相关的Java注解、Spring注解等内容。
3.1 Java注解
JRE的库包 java.lang.annotation中代码包括注解相关的接口、类等内容。接口 java.lang.annotation.Annotation是所有自定义注解自动继承的接口,不需要定义时指定,类似于所有Java类都自动继承的Object类。
3.1.1 Java注解的介绍
注解是一系列元数据,它利用元数据来解释、说明程序代码(即被注解的对象)。但是,注解不是所标注的代码的组成部分。注解对于代码的运行效果没有直接影响。注解的作用包括:
(1) 提供信息给编译器,编译器可以利用注解来探测错误和警告信息。
(2) 软件工具可以利用注解信息来生成代码、HTML文档或者做其他相应处理。
(3) 运行时的处理,某些注解可以在程序运行时接受代码的提取。
在开始学习注解具体语��之前,可以把注解看成一张标签。与接口和类一样,注解也是一种类型。注解是自Java 1.5开始引入的概念,它允许***定义自己的注解类型和使用自定义的注解。注解通过关键字@interface进行定义,代码如例3-1所示。
【例3-1】 注解定义的代码示例。
public @interface TestAnnotation {
}
例3-1中的代码自定义了一个名字为 TestAnnotaion 的注解,该注解自动继承了类java.lang.annotation.Annotation。创建了自定义注解后,就可以使用自定义的注解。注解的使用方法如例3-2所示。
【例3-2】 使用自定义注解的代码示例。
@TestAnnotation
public class Test {
}
例3-2创建了一个类Test,并在类定义的上方加上了@TestAnnotation注解,就意味着用TestAnnotation注解了类Test。
3.1.2 Java的元注解
要想更好地使用注解,还需要理解元注解。元注解是加到注解上的注解,它的目的是解释、说明其他普通注解。元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable共5种。
元注解@Retention应用到一个注解时,说明该注解的存活时间。它的取值包括RetentionPolicy.SOURCE、RetentionPolicy.CLASS、RetentionPolicy.RUNTIME。其中,RetentionPolicy.SOURCE表明注解只在源码阶段保留,在编译器进行编译时被丢弃。RetentionPolicy.CLASS表明注解被保留到编译进行的时候,而不会被加载到 JVM 中。RetentionPolicy.RUNTIME表明注解可以保留到程序运行的时候,它会被加载进入JVM中;在程序运行时可以获取到它们。
应用元注解@Retention的代码示例如例3-3所示,在该示例中设定注解TestAnnotation 可以在程序运行时被获取到。
【例3-3】 应用元注解@Retention的代码示例。
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
元注解@Documented表示注解内容会被Javadoc工具提取成文档,文档内容会因为注解内容的不同而不同。
元注解@Target表示注解用于什么地方,如类型、方法和域等。元注解@Target的取 值包括ElementType.FIELD、ElementType.METHOD、ElementType.PARAMETER、ElementType.CONSTRUCTOR、ElementType.LOCAL_VARIABLE、ElementType.TYPE、ElementType.ANNOTATION_TYPE、ElementType.PACKAGE。其中,ElementType.FIELD表示对字段、枚举常量的注解,ElementType.METHOD表示对方法的注解,ElementType.PARAMETER表示对方法参数的注解,ElementType.CONSTRUCTOR表示对构造函数的注解,ElementType.LOCAL_VARIABLE表示对局部变量的注解,ElementType.ANNOTATION_TYPE表示对注解类型的注解,ElementType.PACKAGE表示对包的注解,ElementType.TYPE表示对接口、类、枚举、注解等任意类型的注解。
被元注解@Inherited注解过的注解作用于父类后,子类会自动继承父类的注解。应用的示例代码如例3-4所示。
【例3-4】 应用元注解@Inherited的代码示例。
//该示例由3部分组成
//定义注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
//在父类中增加注解
@Test
public class A {}
//子类B虽然没有明确给出注解信息,但是它会继承父类A中的注解@Test
public class B extends A {}
元注解@Repeatable是在Java 1.8中引入的注解,其应用的示例代码如例3-5所示。Persons 可以被看作是一张总标签,上面贴满了 Person 这种同类型但内容不一样的标签。于是,可以同时给 SuperMan 贴上画家、程序员、产品经理等标签(即加上3个注解)。
【例3-5】 应用元注解@Repeatable的代码示例。
//Persons是用数组存放注解的容器注解,它里面必须要有一个 value 属性,即数组
@interface Persons {
Person[] value();
}
//可以重复、多次应用Persons注解
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
//不同属性的Person注解
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
注解中可以拥有属性(也叫作成员变量),示例代码如例3-6所示。表示自定义的注解 TestAnnotation注解拥有 id 和 msg 两个属性,返回类型分别为int和String。
【例3-6】 带属性的注解代码示例。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id();
String msg();
}
使用注解时,应该给属性赋值。属性的赋值方法是在注解后面的括号内以“属性=取值”的形式进行的,多个属性的赋值之间用逗号隔开,示例代码如例3-7所示。需要注意的是,注解中属性的类型只能是 8 种基本数据类型和类、接口、注解及它们的数组。
【例3-7】 给注解属性赋值的应用代码示例。
@TestAnnotation(id=3,msg="hello annotation")
public class Test {
}
注解中属性可以有默认值,默认值需要用关键字 default 指定,示例代码如例3-8所示。注解TestAnnotation 中属性id 的默认值被指定为 ?1,属性msg的默认值被指定为"Hi"。
【例3-8】 给注解属性指定默认值的代码示例。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
public int id() default -1;
public String msg() default "Hi";
}
假如指定了属性默认值,可以不用再在注解TestAnnotation 后面的括号内对属性进行赋值。示例代码如例3-9所示。
【例3-9】 属性有默认值的注解应用代码示例。
@TestAnnotation()
public class Test {}
如果注解只有一个属性时,应用这个注解时则可以省略属性名而将属性值直接填写到注解后面的括号内,如例3-10所示。
【例3-10】 单一属性注解简单应用的代码示例。
@Check("hi")
int a;
例3-11代码和例3-10代码的效果是一样的。
【例3-11】 单一属性注解完整应用的代码示例。
@Check(value="hi")
int a;
如果一个注解没有任何属性,应用这个注解时注解后面的括号则可以省略,如例3-12所示。
【例3-12】 无属性注解的应用代码示例。
@Perform
public void testMethod(){}
3.1.3 Java预置的基本注解
在java.lang包下,Java预先提供了@Deprecated、@SuppressWarnings、@Override、@SafeVarargs、@FunctionalInterface共5个基本注解。
注解@Deprecated是用来标记过时的元素。编译器在编译阶段遇到这个注解时会发出提醒警告,告诉***正在调用一个过时的元素(如过时的方法、类、成员变量)。
注解@SuppressWarnings表示阻止警告的意思。使用@Deprecated 注解后,编译器有时会给***发出警告提醒;当***想忽略警告提醒时,可以通过 @SuppressWarnings 注解达到目的。@SuppressWarnings的参数有deprecation(表示忽略使用了过时元素时的警告)、unchecked(表示忽略执行了未检查转换时的警告)、fallthrough(表示忽略 switch 程序块直接通往下一种情况而没有break 时的警告)、path(表示忽略类路径、源文件路径等路径中有不存在的路径时的警告)、serial(表示忽略可序列化的类缺少 serialVersionUID 定义时的警告)、finally(表示忽略任何 finally 子句不能正常完成时的警告)、all(表示忽略关于所有情况的警告)。
注解@Override表示子类要重写父类(或接口)的对应方法。如果想重写父类的方法,如toString()方法,则在方法前面加上@Override,系统可以帮助检查方法的正确性。示例代码如例3-13所示。
【例3-13】 注解@Override的应用代码示例。
//@Override可以帮助检查方法的正确性
@Override
public String toString(){...} //这是子类方法正确的写法
//下面子类方法是错误的,有@Override,系统可以帮助检查出tostring的拼写错误
@Override
public String tostring(){...}
//下面子类方法是错误的,由于没有@Override,系统不会帮助检查出tostring的拼写错误
public String tostring(){...}
注解@SafeVarargs被用来标识参数**类型,它被用来提醒***不要用参数做一些不**的操作,它的存在会阻止编译器产生 unchecked 这样的警告。它是在 Java 1.7 中引入的注解。
注解@FunctionalInterface被用来指定某个接口必须是函数式接口,否则就会编译出错。@FunctionalInterface是 Java 1.8 引入的新特性。如果接口中有且只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口称为函数式接口。