开发者

SpringBoot自定义注解如何解决公共字段填充问题

目录
  • 1.1 问题分析
  • 1.2 实现思路
  • 1.3 代码开发
    • 1.3.1 步骤一
    • 1.3.2 步骤二
    • 1.3.3 步骤三
  • 总结

    1.1 问题分析

    新增员工或者新增菜品分类时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工或者编辑菜品分类时需要设置修改时间、修改人等字段。

    这些字段属于公共字段,也就是也就是在我们的系统中很多表中都会有这些字段,如下:

    序号字段名含义数据类型
    1create_time创建时间datetime
    2create_user创建人idbigint
    3update_time修改时间datetime
    4update_user修改人idbigint

    而针对于这些字段,我们的赋值方式为:

    1). 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。

    2). 在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户ID。

    目前,在我们的项目中处理这些字段都是在每一个业务方法中进行赋值操作,如下:

    新增员工方法:

    	/**
         * 新增员工
         *
         * @param employeeDTO
         */
        public void save(EmployeeDTO employeeDTO) {
            //.......................
    		//
            //设置当前记录的创建时间和修改时间
            employee.setCreateTime(LocalDateTime.now());
            employee.setUpdateTime(LocalDateTime.now());
    
            //设置当前记录创建人id和修改人id
            employee.setCreateUser(BaseContext.getCurrentId());//目前写个假数据,后期修改
            employee.setUpdateUser(BaseContext.getCurrentId());
    		///
            employeeMapper.insert(employee);
        }

    编辑员工方法:

    	/**
         * 编辑员工信息
         *
         * @param employeeDTO
         */
        public void update(EmployeeDTO employeeDTO) {
           //........................................
    	   ///
            employee.setUpdateTime(LocalDateTime.now());
            employee.setUpdateUser(BaseContext.getCurrentId());
           ///
    
            employeeMapper.update(employee);
        }

    新增菜品分类方法:

    	/**
         * 新增分类
         * @param categoryDTO
         */
        public void save(CategoryDTO categoryDTO) {
           //....................................
           //
            //设置创建时间、修改时间、创建人、修改人
            category.setCreateTime(LocalDateTime.now());
            category.setUpdateTime(LocalDateTime.now());
            category.setCreateUser(BaseContext.getCurrentId());
            category.setUpdateUser(BaseContext.getCurrentId());
            ///
    
            categoryMapper.insert(category);
        }

    修改菜品分类方法:

    	/**
         * 修改分类
         * @param categoryDTO
         */
        public void update(CategoryDTO categoryDTO) {
            //....................................
            
    		//
            //设置修改时间、修改人
            category.setUpdateTime(LocalDateTime.now());
            category.setUpdateUser(BaseContext.getCurrentId());
            //
    
            categoryMapper.update(category);
        }

    如果都按照上述的操作方式来处理这些公共字段, 需要在每一个业务方法中进行操作, 编码相对冗余、繁琐,那能不能对于这些公共字段在某个地方统一处理,来简化开发呢?

    答案是可以的,我们使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。

    1.2 实现思路

    在实现公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。

    在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:

    序号字段名含义数据类型操作类型
    1create_time创建时间http://www.devze.comdatetimeinsert
    2create_user创建人idbigintinsert
    3update_time修改时间datetimeinsert、update
    4update_user修改人idbigintinsert、update

    实现步骤:

    1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法

    2). 自定义切面类 AutoFillASPect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值

    3). 在 Mapper 的方法上加入 AutoFill 注解

    若要实现上述步骤,需掌握以下知识(之前课程内容都学过)

    **技术点:**枚举、注解、AOP、反射

    1.3 代码开发

    按照上一小节分析的实现步骤依次实现,共三步。

    1.3.1 步骤一

    自定义注解 AutoFill

    进入到sky-server模块,创建com.sky.annotation包。

    package com.sky.annotation;
    
    import com.sky.enumeration.OperationType;
    import Java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoFill {
        //数据库操作类型:UPDATE INSERT
        OperationType value();
    }

    其中OperationType已在sky-common模块中定义

    package com.sky.enumeration;
    
    /**
     * 数据库操作类型
     */
    public enum OperationType {
    
        /**
         * 更新操作
         */
        UPDATE,
    
        /**
         * 插入操作
         */
        INSERT
    }

    1.3.2 步骤二

    自定义切面 AutoFillAspect

    在sky-server模块,创建com.sky.aspect包。

    package com.sky.aspect;
    
    /**
     * 自定义切面,实现公共字段自动填充处理逻辑
     */
    @Aspect
    @Component
    @Slf4j
    public class AutoFillAspect {
    
        /**
         * 切入点
         */
        @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
        public void autoFillPointCut(){}
    
        /**
         * 前置通知,在通知中进行公共字段的赋值
         */
        @Before("autoFillPointCut()")
        public void autoFill(JoinPoint joinPoint){
            /重要
            //可先进行调试,是否能进入该方法 提前在mapper方法添加AutoFill注解
            log.info("开始进行公共字段自动python填充...");
    
        }
    }

    完善自定义切面 AutoFillAspect 的 autoFill 方法

    package com.sky.aspect;
    
    import com.sky.annotation.AutoFill;
    import com.sky.constant.AutoFillConstant;
    import com.sky.context.BaseContext;
    import com.sky.enumeration.OperationType;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    import java.lang.reflect.Method;
    import java.time.LocagioYZPwlDateTime;
    
    /**
     * 自定义切面,实现公共字段自动填充处理逻辑
     */
    @Aspect
    @Component
    @Slf4j
    public class AutoFillAspect {
    
        /**
         * 切入点
         */
        @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
        public void autoFillPointCut(){}
    
        /**
         * 前置通知,在通知中进行公共字段的赋值
         */
        @Before("autoFillPointCut()")
        public void autoFill(JoinPoint joinPoint){
            log.info("开始进行公共字段自动填充...");
    
            //获取到当前被拦截的方法上的数据库操作类型
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
            AutoFill autoFill = signature.getMethod().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){
                //为4个公共字段赋值
                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){
                //为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();
                }
            }
        }
    }

    1.3.3 步骤三

    在Mapper接口的方法上加入 AutoFill 注解

    CategoryMapper为例,分别在新增和修改方法添加@AutoFill()注解,也需要EmployeeMapper做相同操作

    package com.sky.mapper;
    
    @Mapper
    public interface CategoryMapper {
        /**
         * 插入数据
         * @param category
         */
        @Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, jsupdate_user)" +
                " VALUES" +
                " (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
        @AutoFill(value = OperationType.INSERT)
        void insert(Category category);
        /**
         * 根据id修改分类
         * @param category
         */
        @AutoFill(value = OperationType.UPDATE)
        void update(Category category);
    
    }

    同时,将业务层为公共字段赋值的代码注释掉。

    1). 将员工管理的新增和编辑方法中的公共字段赋值的代码注释。

    2). 将菜品分类管理的新增和修改方法中的公共字段赋值的代码注释。

    总结

    以上为个人经验,希望js能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜