AOP切面公共字段自动填充

目录

项目场景目标:

实现思路:

实现过程:

<面试题>


在使用 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主要通过以下几个概念来实现
    1. 切面(Aspect):由切点和通知组成第一个横切逻辑单元,可以看做是一种特殊的类
    2. 切点(Pointcut);指定在哪些程序执行点上插入额外的行为,通常使用表达式来定义切点
    3. 通知(Advice):插入到切点上的额外行为,通常包括前置通知、后置通知、异常通知、环绕通知等
    4. 织入(Weaving):将切面与目标对象合并,生成一个新的代理对象,并在运行期间插入额外行为
  • 在Spring框架中,AOP可以应用于各种场景,如事务管理、缓存、日志、权限控制等,可以大大简化程序设计和代码维护工作,同时Spring框架还提供了各种AOP相关的注解和工具类,使得AOP的使用变得更加简单和方便

 

Spring AOPAspectJ AOP有什么区别

  • SpringAOPAspectJ AOP都是AOP实现的,它们都可以在运行时动态地为程序添加额外的行为,但是他们之间有如下几个区别
    1. 实现方式不同SpringAOP是基于JDK动态代理CGLIB的代理机制实现的,而AspectJ AOP则是基于字节码操纵实现的
    2. 支持的切点表达式不同AspectJ AOP支持更加强大和灵活的切点表达式,例如正则表达式、类型模式匹配、注解匹配等,而SpringAOP仅支持基于方法名、参数列表和异常类型的切点表达式
    3. 适用范围不同SpringAOP主要适用于Spring框架中的IOC容器和SpringMVC中的Web应用程序,而AspectJ AOP则是一个独立的AOP框架,可以用于任何Java应用程序
    4. 集成方式不同Spring AOP可以与Spring框架的其他特性(如IOC、事务管理等)无缝集成,而AspectJ AOP需要通过AspectJ编译器来实现
  • 27
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值