spring boot中的aop
切面(Aspect)横切关注点的模块化封装,通常是一个类,包含了通知(Advice)和切点(Pointcut)的定义。例如,“日志切面” 可以封装所有日志记录的逻辑。
通知(Advice)切面中具体的 “动作”,即横切逻辑的实现。根据执行时机不同,分为 5 种类型:
前置通知(Before):目标方法执行前执行。
后置通知(After):目标方法执行后执行(无论是否抛出异常)。
返回通知(AfterReturning):目标方法正常返回后执行。
异常通知(AfterThrowing):目标方法抛出异常后执行。
环绕通知(Around):包围目标方法执行,可在方法前后自定义逻辑,甚至控制方法是否执行。
切点(Pointcut)定义 “哪些方法需要被切面拦截”,通常通过表达式(如 AspectJ 表达式)指定。例如,“拦截所有 service 包下的方法”。
连接点(Join Point)程序执行过程中可被拦截的 “时机”,如方法调用、字段访问等。在 Spring AOP 中,连接点通常指方法的执行。
二、Spring AOP 的实现原理
Spring AOP 是 Java 中最主流的 AOP 实现,它基于动态代理
三、Spring AOP 的使用步骤
以 Spring Boot 环境为例,演示 AOP 的基本使用:
1. 引入依赖
在 pom.xml 中添加 Spring AOP 依赖(Spring Boot 已内置,无需额外版本号):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
导入出问题,可以-u,强制更新快照版本(Snapshot)依赖;
2. 定义切面类
通过 @Aspect 注解标记切面类,并用 @Component 注册到 Spring 容器
---------------------------------
例子: 苍穹外卖中切面编程的应用(与普通的aop不同,使用了注解结合的方法):
一,首先定义了注解类:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:update insert
OperationType value(); //这里的OperationType是自己定义的类型
//这里定义了方法,只需要在使用注解时传入方法对应的参数即可;
}
------------------------------
二,定义切面类(记得添加component和aspect注解):
@Component
@Aspect
@Slf4j //用于生成日志
public class AutoFillAspect {
/**
* 切入点
*/
/*execution中前面的*表示修饰符,例如int long;*/
@Pointcut("execution(* com.sky.mapper.*.*(..) ) && @annotation(com.sky.annotation.AutoFill)")
//表示只有mapper包下的类并且加入了AutoFill注解才会被识别
public void autoFillPointCut() {
}
/**
* 前置通知
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
log.info("开始公共字段自动填充数据");
//1.获取当前操作的类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取当前拦截的方法签名
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解签名
OperationType operationType = autoFill.value();//获取数据库的操作类型
//2.获取当前拦截方法的参数-实体对象
Object[] args = joinPoint.getArgs();
Object entity = args[0];//实体对象,不确定是哪个对象,所以用object
//3.准备赋值的数据
LocalDateTime now = LocalDateTime.now();
long currentId = BaseContext.getCurrentId();
//4.根据当前操作的类型,为实体对象的公共字段赋值(反射)
if (operationType == OperationType.INSERT) {
try {
Method setCreatTime = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreatUser = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//赋值
setCreatTime.invoke(entity, now);
setCreatUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
} else if (operationType == OperationType.UPDATE) {
try {
Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//赋值
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
-------------------------------------------------
三,具体使用类 (只需要在方法上添加注解并传入参数即可):
例如:
@AutoFill(value = OperationType.UPDATE)
void update(Dish dish);
---------------------------------------------------------------------------
aop中的切入点表达式:
二、常用切入点表达式类型及语法:
1. execution():匹配方法执行(最常用)
语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数类型) [throws 异常])
各部分说明:
修饰符:可选,如 public、private,省略时匹配任意修饰符(* 表示任意)。
返回值类型:必填,* 表示任意返回值;具体类型如 String、void 等。
包名。类名:必填,支持通配符:
*:匹配任意单个包 / 类(如 com.example.service.* 匹配 service 包下的所有类)。
..:匹配任意层级的子包或类(如 com.example..* 匹配 com.example 下所有子包的类)。
方法名:必填,* 表示任意方法;get* 表示所有以 get 开头的方法。
参数类型:必填,() 表示无参;(..) 表示任意参数(数量、类型不限);(String) 表示单个 String 参数;(String, int) 表示 String + int 参数。
throws 异常:可选,指定方法抛出的异常,省略时匹配任意异常
-------------------------------------------------------
连接点功能:
例子:
@Aspect
@Component
@Slf4j
public class AOP {
@Pointcut("@annotation(com.xiangxiang.autoFill.AutoFill)")
public void pointcut(){
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint jointPoint) throws Throwable {
//获取类名
String className = jointPoint.getTarget().getClass().getName();
//获取方法名
String methodName = jointPoint.getSignature().getName();
//获取参数
Object[] args = jointPoint.getArgs();
//获取返回值(只有around才有这个功能)
Object result = jointPoint.proceed();
return result;
}
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Hexo!

