文章目录
1 AOP
1.1 AOP相关概念回顾(第一弹)
前面我们介绍过AOP相关概念:参考SpringBoot With IoC,DI, AOP,自动配置
1.1.1 常见通知类型
通知类型 | 解释 |
---|---|
@Before | 前置通知,切入的方法前AOP中声明方法生效 |
@After | 后置通知 |
@Around | 环绕通知 |
@AfterReturning | 返回后通知,方法返回值后AOP中声明方法生效 |
1.1.2 切入点表达式
例如:想切入到service的impl包下,任意方法,且被@AuthFill注解声明的方法,请写出切点表达式:
@Pointcut("execution(* com.sky.service.impl.*.*(..))&& @annotation(com.sky.annotation.AutoFill)")
其中:
特殊字符 | 解释 |
---|---|
* com | * 表示任意返回值 |
impl.* | * 表示任意类 |
.*(…) | * 表示任意方法 |
.*(…) | (…)表示任意类型的参数 |
1.1.3 AOP的核心概念
名词 | 解释 |
---|---|
连接点 | 所有可以进行功能增强的方法 |
切入点 | 进行了功能增强的方法 |
通知 | AOP中所定义的将要被通用的功能 |
切面 | 切入点+通知 |
目标对象 | 将要使用AOP中定义方法的对象 |
切入点表达式 | 指定那些方法是切入点 |
1.2 AOP使用入门(切入点通过注解声明)
1.2.1 自定义枚举
此处采用分模块开发,将属性名称进行抽离,便于后边各类场景中使用。
/**
* 数据库操作类型
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
1.2.2 自定义注解
自定义注解中,对
何时执行何种自动填充
用value值做限制,便于在切面类定义各种通知生效场景
。
/**
* 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
OperationType value();
}
1.2.3 自定义切面
反射:使用反射获得具体是insert还是update方法,然后进行执行相关对应通知。
/**
* 自定义切面,实现公共字段自动填充处理逻辑
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
/**
* 切入点
*/
//抽取冗余切入点表达式
@Pointcut("execution(* com.sky.mapper.*.*(..))&& @annotation(com.sky.annotation.AutoFill)")
public void authFillPointCut() {
}
@Before("authFillPointCut()")
public void before(JoinPoint joinPoint) {
log.info("进行了公共字段填充");
//Signature signature1 = joinPoint.getSignature();//签名对象 为什么 不用Signature获取对象?原因Signature中无getMethod方法,我们需要通过getMethod获取到方法名,从而获取到方法上的注解
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象,获得方法对象
//AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
Method method = signature.getMethod();//获得具体方法
AutoFill autoFill = method.getAnnotation(AutoFill.class);//获得方法上的注解
OperationType operationType = autoFill.value(); //获取数据库操作类型
Object[] args = joinPoint.getArgs();//获取到当前被拦截的方法的参数--实体对象
if (args == null || args.length == 0){
return;
}
Object entity = args[0];//约定需要自动填充的对象,放在第一位
//准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//根据当前不同的操作类型,为对于的属性通过反射来赋值
if (operationType == 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,now);
setCreateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}else if (operationType == OperationType.UPDATE){
//为更新操作的两个字段进行赋值 //TODO
try {
Method setUpdateTime= entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.2.4 切入点上声明注解
自定义注解:中声明操作类型,调用切面类中不同通知。
/**
* 新增分类
* @param categoryBuild
*/
@Insert("insert into category (type, name, sort, status, create_time, update_time, create_user, update_user) " +
"values (#{type},#{name},#{sort},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})")
@AutoFill(value = OperationType.INSERT)
void insertCategory(Category categoryBuild);