在SpringBoot相关开发中,我们常常用到各种注解。如@Aotuwired,@Data等等,极大的提高了开发效率,降低代码耦合度。
往往官方只提供常用注解,而自己稀奇古怪的想法就需要自定义开发。
注解分类
@Target(ElementType.?)
通过Java盒子类型以及常用注解可以推断出几种注解 :
- 类注解 如:@Data
- 方法注解、 如:@GetMapping
- 属性注解、 如:@Value
- 形参注解、 如:@RequestBody
@类注解
public class 注解分类{
@属性注解
public String 属性;
@方法注解
public void 方法(){
}
public void 方法(@参数注解 String 参数)
}
参考Demo
@Target(ElementType.METHOD)
public @interface InterDemo {
String value() default "";
}
源码
而根据源码中的枚举类型可以看到,Java代码类型均有对应的类型匹配。
//---------------------------------------源码---------------------------------
public enum ElementType {
/** Class, interface (including annotation interface), enum, or record
* declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation interface declaration (Formerly known as an annotation type.) */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE,
/**
* Module declaration.
*
* @since 9
*/
MODULE,
/**
* Record component
*
* @jls 8.10.3 Record Members
* @jls 9.7.4 Where Annotations May Appear
*
* @since 16
*/
RECORD_COMPONENT;
}
注解存在多久
为什么这么问?
每次项目启动都会出现一个文件夹 out/target ,里面的代码为class文件,在计算机中只有0|1。那么注解将被解析成什么,注解存在到什么时候?
生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。
public enum RetentionPolicy {
//注解将被编译器忽略掉
SOURCE,
//注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留
CLASS,
//注解将被编译器记录在class文件中,并且运行时会被虚拟机保留,能通过反射被读取
RUNTIME
}
一般常用 : RUNTIME。
注解怎么用,如何生效
我要给注解添加功能,功能在哪添加呢。注解放到类上,是不是包裹一个类呢?放到方法上呢?既然包裹住,那就有执行前和执行后,为什么不用切面当作注解的功能区
基本配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Aspect
@Component
public class AspectDemo {
}
定义切入点
@Pointcut("@annotation(文件路径)") 路径参考 :life.xuxie.www.inter.InterDemo
@Aspect
@Component
public class AspectDemo {
//定义切入点
@Pointcut("@annotation(life.xuxie.www.inter.InterDemo)")
public void interDemoPointcut() {}
}
使用如 @Before 和 @After 注解来定义切面前置和后置通知
@Slf4j
@Aspect
@Component
public class AspectDemo {
@Pointcut("@annotation(life.putl.wx.inter.InterDemo)")
public void interDemoPointcut() {}
@Before("interDemoPointcut()")
public void before() {
System.out.println("Before");
}
@After("interDemoPointcut()")
public void after() {
System.out.println("After");
}
}
注解参数获取
通过上面的代码可以发现注解生效一部分了,因为我们定义的值根本没用到....
获取参数方法就是上面说的通过反射,我们需要在方法中添加形参
获取方法签名:通过 joinPoint.getSignature() 获取方法签名,并将其转换为 MethodSignature。
获取方法对象:通过 MethodSignature 获取方法对象 Method。
获取注解:通过 method.getAnnotation(InterDemo.class) 获取方法上的 @InterDemo 注解。
提取注解值:从注解对象中提取 value 值,并打印出来。
@Slf4j
@Aspect
@Component
public class AspectDemo {
@Pointcut("@annotation(life.putl.wx.inter.InterDemo)")
public void interDemoPointcut() {}
@Before("interDemoPointcut()")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
InterDemo interDemo = method.getAnnotation(InterDemo.class);
if (interDemo != null) {
String value = interDemo.value();
System.out.println("Before: " + value);
}
}
@After("interDemoPointcut()")
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
InterDemo interDemo = method.getAnnotation(InterDemo.class);
if (interDemo != null) {
String value = interDemo.value();
System.out.println("After: " + value);
}
}
@Around("interDemoPointcut()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
// 执行目标方法
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
log.info("{} executed in {} ms", joinPoint.getSignature(), executionTime);
return result;
}
}