SpringBoot AOP
依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
代码实现
以给公共字段注入值为例
公共字段与枚举类:
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Long createUser;
private Long updateUser;
/**
* 数据库操作类型
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
自定义注解
自定义注解 @AutoFill
/**
* 自定义注解,用于标识某个方法需要进行功能字段自动填充
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:UPDATE、INSERT
OperationType values();
}
AOP实现类
/**
* 自定义切片,实现公共字段字段填充
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
/**
* 切入点
* @Pointcut注解表示切入点的表达式有多种,最常用的是两种,execution表达式和@annotation注解;
* execution(* com.sky.mapper.*.*(..)) 表示 com.sky.mapper包下所有类的所有方法
* @annotation(com.sky.annotation.AutoFill) 表示 com.sky.annotion包下的AutoFill注解
*/
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}
/**
* 前置通知,在通知中进行公共字段赋值
* 可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
* @Before("autoFillPointCut()") 表示接入点为autoFillPointCut()的切入点
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
log.info("开始公共字段自动填充...");
//获取到当前被拦截的方法上的数据库操作类型
MethodSignature signature=(MethodSignature) joinPoint.getSignature();//对象方法签名
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType values = autoFill.values();//获得数据库操作对象
//获取到当前被拦截的方法的参数--实体对象
Object[] args = joinPoint.getArgs();
if(args==null||args.length==0) {return;}
Object entity = args[0];
//准备赋值的数据
LocalDateTime localDateTime = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//根据当前不同的操作类型,为对应属性通过反射来赋值
if(values==OperationType.INSERT){
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setCreateTime.invoke(entity,localDateTime);
setCreateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,localDateTime);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}else if(values==OperationType.UPDATE){
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity,localDateTime);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
核心注解和类
- @Aspect ,表示当前类是一个切面类,用于类上
- @Before ,前置通知,用于方法上,被@Before注解标注的方法会在被切入方法执行之前执行
- @After ,后置通知,用于方法上,被@After 注解标注的方法会在被切入方法执行之后执行
- @AfterRetruning ,返回通知,用于方法上,被@AfterRetruning 注解标注的方法会在被切入方法返回结果之后执行
- @AfterThrowing,异常通知,用于方法上,被@AfterThrowing 注解标注的方法会在被切入方法抛出异常之后执行,一般用于有目的地获取异常信息
- @Aroud ,环绕通知,用于方法上,被@Aroud 注解标注的方法会在被切入方法执行前后执行
- @Pointcut,切入点,标注在方法上,用于定义切入点,切入点就是指哪些连接点初进行切入。@Pointcut注解表示切入点的表达式有多种,最常用的是两种,execution表达式和注解;
-
- execution:用于匹配方法执行的连接点(路径)
- @annotation:用于匹配当前执行方法持有指定注解的方法
- Jointpoint,连接点,连接点是指被aop切面切入的位置。可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
标记切入点的常用方法
execution表达式
表达式法:访问修饰符 返回值 包名.包名...类名.方法(参数列表)
//1. 表示匹配所有com.fanfu包以及子包下的所有类中以add开头的方法,返回值、参数不限;
@Pointcut("execution(* com.fanfu..*.*.add*(..))")
//2. 表示匹配所有com.fanfu包以及子包下的所有类中以add开头,参数类型是String的方法,返回值不限;
@Pointcut("execution(* com.fanfu..*.*.add*(String))")
//3. 表示匹配com.fanfu包下任意类、任意方法、任意参数;
@Pointcut("execution(* com.fanfu..*.*.*(String))")
- execution()为表达式的主体;
- 第一个*表示返回值类型为任意,即不限制返回值类型;
- 包后的*表示当前包,包后面连续两个..表示当前包以及子包;(..)表示任意参数;
- 最后的*.*(..)表示匹配任意类、任意方法、任意参数;
注解
注解语法:@annotation(自定义的注解)
//表示有@AutoFill注解的方法
@Pointcut("@annotation(com.sky.annotation.AutoFill)")