SpringBoot如何自定义注解
在Java中,我们可以创建自定义注解,这是一种应用于类、方法、变量、参数和包等元素的元数据。自定义注解可以帮助我们更好地组织和处理代码。需要注意的是,自定义注解本身不会改变代码的行为。它们只是一种元数据,可以被其他代码读取和处理。要使注解有用,我们需要编写一些处理这些注解的代码,例如反射。
1.注解定义
- Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
- 参数成员只能用public或默认(default)这两个访问权修饰
- 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
- 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法
- 注解也可以没有定义成员, 不过这样注解就没啥用了
2.如何自定义注解
对注解有了一个基本的认识:注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上这些标记,然后程序在编译时或运行时可以检测到这些标记从而执行一些特殊操作。因此可以得出自定义注解使用的基本流程:
第一步,定义注解------相当于定义标记; |
---|
第二步,配置注解------把标记打在需要用到的程序代码中; |
第三步,解析注解------在编译期或运行时检测到标记,并进行特殊操作。 |
3.注解的分类
3.1 jdk的基本注解
① @Override
重写
②@Deprecated
已过时
③@SuppressWarnings(value = "unchecked")
压制编辑器警告
3.2 JDK元注解
@Retention、@Target、@Inherited、@Documented这些注解是元注解,用于修饰我们自定义的注解。
元注解用于修饰其他的注解
① @Retention:定义注解的保留策略
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
② @Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)
@Target(ElementType.TYPE) //接口、类
@Target(ElementType.FIELD) //属性
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE) //局部变量
@Target(ElementType.ANNOTATION_TYPE) //注解
@Target(ElementType.PACKAGE) //包
注:可以指定多个位置,例如:
@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用
③ @Inherited:指定被修饰的Annotation将具有继承性
④@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.
4.通过反射解析自定义注解
- 定义在类上的注解
java
package com.hh.test.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Author {
String name() default "HH";
String email() default "unknown";
}
使用@Author注解
package com.hh.test.annotation;
@Author(name = "wk", email = "xxxxxx@qq.com")
public class Hello {
public static void main(String[] args) {
}
@Log(value = "测试日志写入", type = "2")
public void testLog() {
}
}
通过反射获取注解的值
Class<?> clazz = Hello.class;
if (clazz.isAnnotationPresent(Author.class)) {
Author author = clazz.getAnnotation(Author.class);
System.out.println("Author: " + author.name() + ", Email: " + author.email());
}
-
定义在方法上面的注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Log {
String value() default "";
String type() default "1";
}
使用@Log注解
@Log(value = "测试日志写入", type = "2")
public void testLog() {
}
通过反射获取注解的值
Log log = clazz.getMethod("testLog").getAnnotation(Log.class);
System.out.printf("日志的内容:%s \n", log.value());
System.out.printf("日志的类型:%s", log.type());
5.通过Springboot使用自定义注解
首先,我们创建一个Spring Boot项目,添加maven依赖spring-boot-starter-aop
,spring-boot-starter-aop
模块自身提供了针对 spring-aop
、aspectjrt
和 aspectjweaver
的依赖,包含了我们实现自定义注解的所有功能。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建一个方法级别的注解
import java.lang.annotation.*;
/**
* 定义一个方法级别的@SaveSystemLog注解,用于标注需要监控的方法:
*/
@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface SaveSystemLog {
/**
* 操作名称
*/
String value() default "";
/**
* 日志类别
*/
String type() default "1";
}
创建切面实现类SystemLogAspect
public class SystemLogAspect {
@Pointcut("execution(* com.hh.test.controller.*.*(..)) && @annotation(com.hh.test.annotation.SaveSystemLog)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
Object result = null;
long beginTime = System.currentTimeMillis();
try {
// 执行方法
result = point.proceed();
} catch (Throwable e) {
log.error(e.getMessage());
}
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point, time);
return result;
}
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SaveSystemLog logAnnotation = method.getAnnotation(SaveSystemLog.class);
System.out.println("日志保存成功:"+logAnnotation.value());
}
}
测试一下
public class Test {
@RequestMapping("/log")
@SaveSystemLog(value = "测试日志")
public void saveLog(){
System.out.println("123");
}
}
6.总结
主要是重点关注下@Target
和@Retention
这两个注解。
@Target
注解,他的作用是将这个注解放在什么地方,比如类上、方法上、构造器上、变量上等,他的值是一个枚举类型的。
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
@Retention
注解,它的作用是为了说明这个注解的生命周期,也可以说是注解的保留位置。
在注解中有三个生命周期,即
- RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
- RetentionPolicy.CLASS:注解在编译时被保留(默认的保留策略),但Java VM在运行时不需要它。这意味着这个注解信息不会被加载到JVM中。
- RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;