系统的异步操作日志记录 :自定注解+AOP+异步

操作日志的记录是通过AOP+自定义注解的方式

  1. 在我们的系统中定义了一个Log注解

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface OperLog {
    
        String value() default "";
    }
    
  2. 该注解标记在Contrller接口的方法上,通过切面的方式记录日志,在方法返回和抛异常的时候执行

        /**
         * 新增员工
         * @param employee
         * @param session
         * @return
         */
        @OperLog(value = "新增员工")
        @PostMapping
        public R addEmp(@RequestBody Employee employee, HttpSession session) {
    

    基于方法上注解的参数有操作类型( insert update select等)模块名称等
    在AOP切面中构建操作日志的实体类
    基于request中的请求参数和 方法的返回值 方法抛出的异常信息 为操作日志实体类设置属性

    @Component
    @Aspect
    public class OperationAspect {
        @Autowired
        IOperationLogService operationLogService;
    
        //1.切入点表达式:标识哪些方法是切入点(需要进行增强的方法)
        @Pointcut("@annotation(com.itheima.reggie.aspect.annotation.OperLog)")
        private void operationMethod(){}
    
        //3.切面:如何对哪些方法,进行哪些增强
        @Around("operationMethod()")
        //2.通知:具体的增强代码
        public Object operationLogHandle(ProceedingJoinPoint pjp){
            OperationLog operationLog = new OperationLog();
            //获取目标方法:
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            //获取目标方法上的注解
            OperLog annotation = method.getAnnotation(OperLog.class);
            //获取注解属性
            String value = annotation.value();
    
            operationLog.setOperation_action(value);
            operationLog.setUser_id(ApplicationContext.getEmpId());
    
            //获取目标方法的参数:
            Object[] args = pjp.getArgs();
            Object result = null;
            try {
                //前置增强
                result = pjp.proceed(args);//执行目标方法
                //后置增强
            } catch (Throwable throwable) {
                //throwable.printStackTrace();
                //异常增强:可以在此处抛出自定义异常,交由全局异常处理器进行统一异常处理
                //返回后增强
                new  AsyncManager().execute(new TimerTask() {
                    @Override
                    public void run() {
                        operationLogService.save(operationLog);
                    }
                });
            } finally {
                //返回后增强
                new  AsyncManager().execute(new TimerTask() {
                    @Override
                    public void run() {
                        operationLogService.save(operationLog);
                    }
                });
            }
            return result;
        }
    }
    
  3. 将操作日志实体类入库的操作定义为一个TimeTask任务
    将该任务交由jdk提供的一个ScheduledExecutorService(定时任务线程池)来执行进行插入,这样就避免了系统在插入操作日志时如果出现异常了从而导致业务功能无法正常返回

    /**
     * 异步管理器:
     *     内部维护一个线程池
     */
    public class AsyncManager {
    
        private ScheduledExecutorService  scheduledThreadPool
                = new ScheduledThreadPoolExecutor(50);
        
        /**
         * 使用内部的一个线程,去执行具体的任务
         * @param task
         */
        public void execute(TimerTask task){
            scheduledThreadPool.execute(task); //延迟执行,延迟10毫秒
        
    
  4. 效果
    在这里插入图片描述

你可能感兴趣的:(java,AOP,自定义注解)