1 简介
- JDK1.5引入。
- 用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。
- 以“@注解名”在代码中存在的,
- 根据注解参数个数,分为:标记注解、单值注解、完整注解三类
- 不直接影响到程序的语义,只是作为注解(标识)存在
- 通过反射机制实现对元数据的访问
- 注解级别:源代码(SOURCE)级、class级、运行时(UNTIME)
2 分类
- JDK基本注解
- JDK元注解
- 自定义注解
2.1 基本注解
JDK内置的注解类。如
@Override // 表示继承和覆写
@Deprecated // 表示废弃
@SuppressWarnings(value = "unchecked") // 压制警告
2.2 元注解
使用元注解来定义自己的注解
常用元注解:
- @Retention: 注解的保留策略(SOURCE、CLASS、RUNTIME)
- @Target:注解的作用范围
- @Inherited:注解是否可被继承
- @Documented:注解是否可以被javadoc工具提取成文档
2.2.1 @Retention
@Retention(RetentionPolicy.SOURCE) // 源码级别。不存在于CLASS字节文件中
@Retention(RetentionPolicy.CLASS) // CLASS级别(默认策略)。存在于CLASS字节文件中。无法在运行时获得
@Retention(RetentionPolicy.RUNTIME) // 运行时级别。存在于CLASS字节文件中,可在运行时通过反射获取
2.2.2 @Target
@Target(ElementType.PACKAGE) // 包
@Target(ElementType.MODULE) // 模块。@since 9
@Target(ElementType.TYPE) // 类、接口(包含注解接口)、enum、record
@Target(ElementType.FIELD) // 属性(包含enum constants)
@Target(ElementType.CONSTRUCTOR) // 构造函数
@Target(ElementType.METHOD) // 方法
@Target(ElementType.PARAMETER) // 方法参数
@Target(ElementType.LOCAL_VARIABLE) // 局部变量
@Target(ElementType.ANNOTATION_TYPE) // 注解类
@Target(ElementType.TYPE_PARAMETER) // 类型参数。 @since 1.8
@Target(ElementType.TYPE_USE) // 类型使用。@since 1.8
@Target(ElementType.RECORD_COMPONENT) // record组件。@since 16
可以指定多个位置,如:
@Target({ElementType.METHOD, ElementType.TYPE}) // 表示此注解可以在方法和类上面使用
2.2.3 @Inherited
2.2.4 @Documented
3 示例
github: https://github.com/hi-cooper/cz-tutorials/tree/main/aspect-demo
结合AOP的示例
3.1 代码结构
3.2 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>${project.artifactId}</name>
<groupId>com.taiwii</groupId>
<artifactId>aspect-demo</artifactId>
<version>1.0.0</version>
<description>aspect-demo</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.20.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.20.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.3 声明自定义注解/aspect/CustomAspect
package com.taiwii.aspectdemo.aspect;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PACKAGE,
ElementType.MODULE,
ElementType.TYPE,
ElementType.FIELD,
ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.LOCAL_VARIABLE,
ElementType.ANNOTATION_TYPE,
ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE,
ElementType.RECORD_COMPONENT})
@Inherited
@Documented
public @interface CustomAspect {
/** 描述 */
String description();
}
3.4 /service/package-info.java
@CustomAspect(description = "This is PACKAGE")
package com.taiwii.aspectdemo.service;
import com.taiwii.aspectdemo.aspect.CustomAspect;
3.5 /service/UserService.java
package com.taiwii.aspectdemo.service;
import com.taiwii.aspectdemo.aspect.CustomAspect;
@CustomAspect(description = "This is TYPE")
public class UserService<T> {
@CustomAspect(description = "This is FIELD")
public String name;
@CustomAspect(description = "This is CONSTRUCTOR")
public UserService() {}
@CustomAspect(description = "This is METHOD")
public void test(@CustomAspect(description = "This is PARAMETER") String name) {
@CustomAspect(description = "This is LOCAL_VARIABLE")
String prefix = "Hello, ";
System.out.println(prefix + name);
}
}
3.6 /config/BeanConfig.java
package com.taiwii.aspectdemo.config;
import com.taiwii.aspectdemo.aspect.CustomAspect;
import com.taiwii.aspectdemo.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean
public UserService userService() {
UserService<@CustomAspect(description = "This is TYPE_PARAMETER") String> userService =
new @CustomAspect(description = "This is TYPE_USE") UserService();
return userService;
}
}
3.7 /module-info.java
import com.taiwii.aspectdemo.aspect.CustomAspect;
@CustomAspect(description = "This is MODULE")
open module aspect.demo {
requires spring.boot.autoconfigure;
requires spring.boot;
requires lombok;
requires org.aspectj.weaver;
requires org.slf4j;
requires spring.context;
}
3.8 /aop/UserServiceAOP.java
package com.taiwii.aspectdemo.aop;
import com.taiwii.aspectdemo.aspect.CustomAspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.*;
import java.util.Arrays;
@Slf4j
@Aspect
@Configuration
public class UserServiceAOP {
@Pointcut("execution(* com.taiwii..service..*.*(..))")
public void pointCutMethod() {
}
/**
* getDeclaredAnnotation(): 忽略父类继承
* getAnnotation(): 包含继承
*
* @param joinPoint
* @throws Throwable
*/
@After("pointCutMethod()")
public void doAfter(JoinPoint joinPoint) throws Throwable {
CustomAspect pkg = joinPoint.getSignature().getDeclaringType().getPackage().getAnnotation(CustomAspect.class);
if (null != pkg) {
System.out.println(pkg.description()); // This is PACKAGE
}
CustomAspect module = joinPoint.getSignature().getDeclaringType().getModule().getAnnotation(CustomAspect.class);
if (null != module) {
System.out.println(module.description()); // This is MODULE
}
CustomAspect cls = (CustomAspect) joinPoint.getSignature().getDeclaringType().getAnnotation(CustomAspect.class);
if (null != cls) {
System.out.println(cls.description()); // This is TYPE
}
Field[] fields = joinPoint.getSignature().getDeclaringType().getFields();
Arrays.asList(fields).forEach((field) -> {
CustomAspect item = field.getAnnotation(CustomAspect.class);
if (null != item) {
System.out.println(item.description()); // This is FIELD
}
});
Constructor[] constructors = joinPoint.getSignature().getDeclaringType().getConstructors();
Arrays.asList(constructors).forEach((constructor) -> {
CustomAspect item = (CustomAspect) constructor.getAnnotation(CustomAspect.class);
if (null != item) {
System.out.println(item.description()); // This is CONSTRUCTOR
}
});
CustomAspect method = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CustomAspect.class);
if (null != method) {
System.out.println(method.description()); // This is METHOD
}
Parameter[] params = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameters();
Arrays.asList(params).forEach((param) -> {
CustomAspect item = param.getAnnotation(CustomAspect.class);
if (null != item) {
System.out.println(item.description()); // This is PARAMETER
}
});
}
}
3.9 测试类
package com.taiwii.aspectdemo;
import com.taiwii.aspectdemo.aspect.CustomAspect;
import com.taiwii.aspectdemo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService<@CustomAspect(description = "This is TYPE_PARAMETER") String> userService;
@Test
public void test() {
userService.test("Tom");
}
}
4 示例结果
Hello, Tom
This is PACKAGE
This is MODULE
This is TYPE
This is FIELD
This is CONSTRUCTOR
This is METHOD
This is PARAMETER