使用AOP以及自定义注解实现业务日志的收集

Posted 吹灭读书灯 一身都是月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用AOP以及自定义注解实现业务日志的收集相关的知识,希望对你有一定的参考价值。

直接上代码

自定义注解:MyLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    String value() default "";    //value值是说明,会被记录到数据库里面
}

自定义日志的实体类SysLog

@Data
@TableName("sys_log")
public class SysLog {
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;
    @TableField(value = "visit_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date visitTime;
    @TableField(value = "execution_time")
    private Long executionTime;
    @TableField(value = "email")
    private String email;
    private String ip;
    private String url;
    private String method;
    /**
     * 日志标题
     */
    private String title;
    private String params;
    /**
     * 请求类型
     */
    private String type;
}

编写的切面类:LogAspect

@Aspect
@Order(5)
@Component
public class LogAspect {
    private final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    SysLogService sysLogService;
    //可以直接注入HttpServletRequest 来获取当前的请求
    @Autowired
    HttpServletRequest request;

    private Date visitTime;

    private Class<?> clazz;

    private Method method;

    private String myLogValue;

    @Pointcut("@annotation(com.example.controller.sys.MyLog)")
    public void pointcut() {
    }

    /**
     * 前置通知,在Controller层操作前拦截
     *
     * @param joinPoint 切入点
     */
    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint) throws NoSuchMethodException {
        // 获取当前调用时间
        visitTime = new Date();
        clazz = joinPoint.getTarget().getClass();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        if (methodSignature == null || methodSignature.getMethod().getAnnotation(MyLog.class) == null) {
            return;
        }
        MyLog myLog = methodSignature.getMethod().getAnnotation(MyLog.class);
        myLogValue = myLog.value();
        String methodName = methodSignature.getName();
        Object[] args = joinPoint.getArgs();
        //获取具体执行的方法的Method对象
        if (args == null || args.length == 0) {
            //只能获取无参数的方法
            this.method = clazz.getMethod(methodName);
        } else {
            Class<?>[] classArgs = new Class<?>[args.length];
            for (int i = 0; i < args.length; i++) {
                classArgs[i] = args[i].getClass();
            }
            this.method = clazz.getMethod(methodName, classArgs);
        }
    }


    @After("pointcut()")
    public void doAfter(JoinPoint jp) throws Exception {
        long time = System.currentTimeMillis() - visitTime.getTime();
        String url = "";
        /*获取url*/
        if (clazz != null && method != null && clazz != LogAspect.class) {
            Subject currentUser = SecurityUtils.getSubject();
            String username = (String) currentUser.getPrincipal();
            SysLog sysLog = new SysLog();
            sysLog.setVisitTime(visitTime);
            sysLog.setEmail(username);
            sysLog.setUrl(request.getRequestURL().toString());
            sysLog.setExecutionTime(System.currentTimeMillis() - visitTime.getTime());
            //什么类的什么方法
            sysLog.setMethod("[类名]:" + clazz.
                    getName() + ", [方法名]:" + method.getName());
            sysLog.setTitle(myLogValue);
            Object[] args = jp.getArgs();
            if (args == null || args.length == 0) {
                sysLog.setParams("无参数");
            }
            //设置不能超过500长度,防止插入数据库抛异常
            String s = Arrays.toString(args);
            if (s.length() > 485) {
                s = s.substring(0, 480);
            }
            sysLog.setParams(s);
            sysLog.setType(request.getMethod());
            sysLog.setIp(request.getRemoteAddr());

            System.out.println(sysLog);
            //调用Service完成操作,调用dao将sysLog insert数据库
            sysLogService.save(sysLog);
        }

    }
}

ServiceImpl类:

@Service
public class SysLogServiceImpl implements SysLogService {
    @Autowired
    SysLogMapper sysLogMapper;

    @Override
    public void save(SysLog sysLog) {
        sysLogMapper.insert(sysLog);
    }
	/**
	 * 这里就是一个简单的分页查询了。PageResultDTO是自定义的分页返回类
	 */
    @Override
    public PageResultDTO<SysLog> pageSysLogs(Long pageNum, Long pageSize) {
        Page<SysLog> page = new Page<>(pageNum, pageSize);
        Page<SysLog> sysLogPage = sysLogMapper.selectPage(page, null);
        return PageUtil.convertToPageResult(sysLogPage);
    }
}

使用时,只要把@MyLog注解标在自己想要记录日志的方法上,就可以了。
例如:

    @MyLog("测试aaa接口的日志输出")
    @PostMapping("/aaa")
    public RetJson<Object> aaa(@RequestBody User user){
        return RetJson.success(user);
    }

不过,当我使用AOP之后,发现我代码中使用了Shiro的controller全都404了,很奇怪,后面看别人的博客,改了配置才好了

ShiroConfig中加入配置:

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }

同时我也在application.yml中加入了配置:

  aop:
    auto: true
    proxy-target-class: true

以上是关于使用AOP以及自定义注解实现业务日志的收集的主要内容,如果未能解决你的问题,请参考以下文章

自定义日志注解 + AOP实现记录操作日志

SpringCloud Alibaba微服务实战三十一 - 业务日志组件

Spring AOP 实现写事件日志功能

AOP注解实现日志收集

SpringBoot自定义注解+异步+观察者模式实现业务日志保存

JPOM - AOP+自定义注解实现操作日志记录