目录
在使用 AOP(Aspect-Oriented Programming,面向切面编程)实现公共字段自动填充时,通常的原理是通过定义一个切面(Aspect),在特定的切入点(Join Point)前后执行额外的逻辑,以实现字段填充的功能。这种功能在实际开发中常用于自动填充创建时间、更新时间、操作人等公共字段,从而减少重复代码和提高代码的复用性。
项目场景目标:
1. 在发生insert操作时 需要为字段赋值
2. 在发生insert update操作时 需要为字段赋值
实现思路:
我们为了简便操作就要通过切面这种方式统一处理为这几个字段赋值的操作
比如在持久层Mapper 执行insert, 就可以通过切面拦截这个insert操作
我们还需要设置一个手段, 能够知道当前持久层的操作是否需要为公共字段赋值(一堆连接点怎么找切入点)
我们就是通过 自定义注解 的方式为这个Mapper的方法加入注解, (让这个方法成为切入点)
作为一个标识表示该操作需要赋值 没有标识就不需要
--------------------枚举、注解、AOP、反射
实现过程:
1) 枚举(创建一个枚举类, 包含insert, update, 加在注解类AutoFill内部,用以区分是需要为四个字段还是两个字段赋值)
2).自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
3). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
/**
* 自定义切面,实现公共字段自动填充处理逻辑
*/
@Aspect //标识当前类是一个AOP类
@Component //把当前类交给Spring容器去管理
@Slf4j //方便记录一些日志
public class AutoFillAspect {
/**
* 切入点:哪些方法需要被拦截
* mapper包下所有的类所有的方法,同时还要满足这个方法上加入了AutoFill注解
*/
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}
/**
* 通知:代码增强的业务逻辑部分
* 应该用的是前置通知,在执行insert和update方法之前,需要为这几个公共字段赋上值。
* 如果sql执行完毕后在赋值就没有意义了。
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
log.info("开始进行公共字段自动填充...");
//1.获取到当前被拦截的方法上的数据库操作类型 需要转化为它的子接口MethodSignature
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType operationType = autoFill.value();//获得数据库操作类型
/*
* 2.获取到当前被拦截的方法的参数--实体对象
* 约定:如果你想要实现自动填充的话,一定要保证这个实体对象放在第一个参数位置,
* 因为我们接下来要获取的话就获取第一个就可以了。
* */
Object[] args = joinPoint.getArgs();
if(args == null || args.length == 0){ //当前方法没有参数,后续代码没必要执行了,防止出现空指针
return;
}
/*
* 取出第一个参数,之前已经做了约定 实体放在第一个参数位置
* 注意:不要使用Employee来接收,因为这个实体类型是不确定的,现在员工管理是
* Employee实体类接收参数,后期在分类功能中使用的是Category实体类接收参数,
* 菜品mapper中传入的是菜品的实体。所以这个地方使用Object来接收。
*
* */
Object entity = args[0];
//3.准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//4.根据当前不同的操作类型,为对应的属性通过反射来赋值 OperationType:之前创建的枚举
if(operationType == OperationType.INSERT){
//为4个公共字段赋值
try {
//通过实体类的set方法为公共的属性赋值,所以需要先获取对应的set方法对象
// 参数:方法名,方法的形参类型列表 此方法需要处理异常
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);
//通过反射为对象属性赋值 方法对象.invoke:调用对应的方法
// 参数:为哪个对象,具体赋的值
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){
//为2个公共字段赋值
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,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
4). 在 Mapper 的方法上加入 AutoFill 注解
---------------------------------------------------------------------------------------------------------------------------------
<面试题>
- 在Spring框架中,AOP主要通过以下几个概念来实现
切面(Aspect)
:由切点和通知组成第一个横切逻辑单元,可以看做是一种特殊的类切点(Pointcut)
;指定在哪些程序执行点上插入额外的行为,通常使用表达式来定义切点通知(Advice)
:插入到切点上的额外行为,通常包括前置通知、后置通知、异常通知、环绕通知等织入(Weaving)
:将切面与目标对象合并,生成一个新的代理对象,并在运行期间插入额外行为
- 在Spring框架中,AOP可以应用于各种场景,如事务管理、缓存、日志、权限控制等,可以大大简化程序设计和代码维护工作,同时Spring框架还提供了各种AOP相关的注解和工具类,使得AOP的使用变得更加简单和方便
Spring AOP
和AspectJ AOP
有什么区别
SpringAOP
和AspectJ AOP
都是AOP实现的,它们都可以在运行时动态地为程序添加额外的行为,但是他们之间有如下几个区别实现方式不同
:SpringAOP
是基于JDK动态代理和CGLIB的代理机制实现的,而AspectJ AOP
则是基于字节码操纵实现的支持的切点表达式不同
:AspectJ AOP
支持更加强大和灵活的切点表达式,例如正则表达式、类型模式匹配、注解匹配等,而SpringAOP
仅支持基于方法名、参数列表和异常类型的切点表达式适用范围不同
:SpringAOP
主要适用于Spring框架中的IOC容器和SpringMVC中的Web应用程序,而AspectJ AOP
则是一个独立的AOP框架,可以用于任何Java应用程序集成方式不同
:Spring AOP
可以与Spring框架的其他特性(如IOC、事务管理等)无缝集成,而AspectJ AOP
需要通过AspectJ编译器来实现