什么是注解
同 class 和 interface 一样 注解属于一种类型 它在Java1.5版本中开始引入的概念
注解的定义
注解通过 @interface 关键字进行定义 它的形式跟接口类似 ( IEAD创建注解如下图 )
元注解
元注解是可以注解到注解上的注解
很绕口 简单的理解就是 在我们自定义注解上使用的Java API注解
元注解有 @Retention ,@Document ,@Target ,@Inherited ,@Repeatable
1. @Retention (常用)
Retention的英文意为保留期 当 @Retention 应用到一个注解上的时候,它解释说明这个注解的存活时间 其取值范围如下
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到Jvm中
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候 它会被加载到Jvm中 所以程序运行时获取到它
2. @Documented
它的作用是能够将注解的元素包含到JavaDoc中
3. @Target (常用)
指定注解可以运用到那些地方
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注
ElementType.TYPE 可以给一个类型进行注解 比如类 接口 枚举
4. @Inherited
是继承的意思 但它并不是说注解本身可以继承 ,而是说如果一个超类被@Inherited注解过的注解进行注解的话那么它的子类没有任何注解应用的话,那么这个子类就继承了超类的注解
5. @Repeatable
@Repeatable 是Java 1.8才有的 是重复的意思 我觉得这篇文章写得简单易懂 传送门
注解的属性
注解的属性也叫成员变量,注解只能有成员变量,没有方法,注解的成员变量在注解的定义中以为 无参数的方法 形式来声明其方法名定义了该成员变量的名字,其发挥值定义了该成员变量的类型
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log{
int id();
String msg();
}
Log这个注解中有 id 和 msg 两个属性 赋值方式参见如下代码
@Log(id = 3,msg = "hello")
public class Test{
}
需要注意的是,在注解中定义属性时它的类型必须是8种基本数据类型外加类、接口、注解 及它们的数组
注解中的默认值可以使用 defaault 指定 参见如下代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log{
public int id() default -1;
public String msg() default "requestMessage";
}
注解的使用与获取
首先我们定义一个Log注解
package com.it520.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
// 定义注解的msg()属性 默认值为requestMessage
String msg() default "requestMessage";
}
然后我们去使用这个注解
package com.it520.annotation;
import org.junit.jupiter.api.Test;
@Log // 因为该注解有默认值 所以我们在此处不需要指定其值
public class LogTest {
@Test
public void TEST_01() {
// 首先判断这个类上是否包含注解
boolean hasAnnotation = LogTest.class.isAnnotationPresent(Log.class);
if (hasAnnotation) { // 如果有注解
// 通过反射获取注解
Log log = LogTest.class.getAnnotation(Log.class);
System.out.println("msg:" + log.msg());
}
}
}
打印结果为 msg:requestMessage
getAnnotation()源码简要剖析
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
@SuppressWarnings("unchecked")
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
// 如果该注解类不存在 则抛出空指针异常
Objects.requireNonNull(annotationClass);
// 主要是这段代码
return (A) annotationData().annotations.get(annotationClass);
}
(A) annotationData().annotations.get(annotationClass)
① annotationData() 其实就是通过反射去获取一个AnnotationData对象
② AnnotationData 是Class类的一个静态内部类 该类从Java1.5以后存在 (源码部分我加了适当中文注解 这个类很简单 就三个属性 一个构造方法)
private static class AnnotationData {
// 这是一个map集合 装载了类中的所有注解 并且该属性使用了final修饰 不可修改其值
final Map<Class<? extends Annotation>, Annotation> annotations;
// 同上
final Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
// Value of classRedefinedCount when we created this AnnotationData instance
// Class类中给出其默认值为 0
final int redefinedCount;
// 这是该类的构造方法
AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations,
Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
int redefinedCount) {
this.annotations = annotations;
this.declaredAnnotations = declaredAnnotations;
this.redefinedCount = redefinedCount;
}
}
所以 annotationData().annotations 其实返回的是一个装载了该类所有注解的 map 集合
③ 最后通过 map 集合的 get 方法去获取这个注解类
④ 拿到这个注解类后 我们就可以获取里面的注解属性 与Java类是一样的
注解在SpringBoot工程中的使用
1. 首先我们创建一个SpringBoot工程 导入如下依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
2. 在application.yml中配置端口号
server:
port: 8099
3. 自定义一个注解
package com.it520.annotation.csutom;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 玛丽莲梦明
* @描述 自定义注解
*/
// 指定注解在运行时获取
@Retention(RetentionPolicy.RUNTIME)
// 声明注解可以使用在类上 可以使用在方法上
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface RequestLog {
// 请求的方法名
String requestMethod() default "";
// 请求方式类型
String requestType() default "";
}
4. 创建Controller
package com.it520.annotation.csutom;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Method;
/**
* @author 玛丽莲梦明
* @描述 注解测试 获取方法上注解的内容 获取方法上的注解内容
*/
@RestController
@RequestMapping("annotation")
public class LogController {
@GetMapping("/log")
@RequestLog(requestMethod = "annotationTest", requestType = "GET")
public String annotationTest() {
// 通过反射获取类注解上的内容
RequestLog log = LogController.class.getAnnotation(RequestLog.class);
System.out.println(log.requestMethod());
System.out.println(log.requestType());
// 通过反射获取到方法注解上的内容
Method[] methods = LogController.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("annotationTest")) {
RequestLog ann = method.getAnnotation(RequestLog.class);
System.out.println(ann.requestType());
System.out.println(ann.requestMethod());
}
}
return "RequestLog";
}
}
5.启动工程 并通过浏览器访问 localhost:8099/annotation/log
打印结果如下 可以看到我们已经获取到类上和方法上的注解属性值了
很多同学想问 获取到了有什么用啊
在实际工作中注解的获取一般不会在Controller中,我这里是为了演示方便
举个栗子 我们的Log注解 就可以在 HandlerInterceptor 处获取并记录用户访问日志
以上 感谢~!