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

写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!

本篇记录一下项目中使用自定义注解实现日志记录,对自定义注解的原理大家感兴趣的可以自行百度,先对本篇涉及的几个注解简单介绍下:
@Target:表示该注解的作用域,值有TYPE, METHOD, CONSTRUCTOR, FIELD等,我们常用FIELD和METHOD,表示作用在java bean的字段和作用在方法层面上。

@Retention:表示注解生效的时机,它接收RetentionPolicy参数,可能的值有SOURCE, CLASS, 以及RUNTIME,我们常用runtime,表示在编译以及java vm都会保存,所以可以用来反射阶段获取字段的额外属性值。

@Aspect  使用此注解,使标注的类成为切面类,从而获得通知。

@AfterReturning:后置通知,在目标方法执行后执行。

@AfterThrowing:处理程序中未处理的异常, 可以通过throwing属性指定一个形参名,该形参可用于访问目标方法抛出的异常。

测试的基础环境还是使用springboot整合logback的环境,直接贴代码:

自定义一个注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperateLog {
    String title() default "";
}

声明一个切面,具体看注释:

@Component
@Aspect
public class SysOperateLogAspect {

    private  final Logger logger = LoggerFactory.getLogger(SysOperateLogAspect.class);

    @Autowired
    private SysOperateLogService sysOperateLogService;

    /**
     * 定义切点,在注解的位置切入代码
     */
    @Pointcut("@annotation(com.example.springbootmybatis.annotation.OperateLog)")
    public void logPointcut(){}

    /**
     * 定义切面,配置通知
     */
    @AfterReturning(value = "logPointcut()")
    public void saveSysLog(JoinPoint joinPoint) {
        handleLog(joinPoint, null);
    }

    @AfterThrowing(value = "logPointcut()", throwing = "e")
    protected void doAfterReturning(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e);
    }

    /**
     * 记录操作日志
     * @param joinPoint
     * @param e
     */
    protected void handleLog(final JoinPoint joinPoint, final Exception e) {
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取操作
            OperateLog operateLog = method.getAnnotation(OperateLog.class);
            if (operateLog == null) {
                return;
            }
            // 获取当前的用户id
            Integer currentUser = 1;
            //获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            //获取请求的方法名
            String methodName = joinPoint.getSignature().getName();
            //请求的参数
            Object[] args = joinPoint.getArgs();
            //将参数所在的数组转换成json
            String params = JSON.toJSONString(args);
            // 请求的ip
            ServletRequestAttributes servletRequestAttributes =
                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (servletRequestAttributes == null) {
                logger.debug("请求异常,请稍后重试");
            }
            HttpServletRequest request = servletRequestAttributes.getRequest();
            String ip = IpUtils.getIpAddr(request);
            // 设置日志参数
            SysOperateLog sysLog = new SysOperateLog();
            sysLog.setUserId(currentUser);
            sysLog.setOperUrl(StringUtils.substring(request.getRequestURI(), 0, 250));
            sysLog.setOperMethod(className + "." + methodName + "()");
            sysLog.setOperParams(params);
            sysLog.setOperIp(ip);
            sysLog.setTitle(operateLog.title());
            sysLog.setStatus(OperateStatus.SUCCESS.getCode());
            if (null != e) {
                sysLog.setStatus(OperateStatus.FAIL.getCode());
                sysLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            sysOperateLogService.saveSysOperateLog(sysLog);
        } catch (Exception e1) {
            logger.error("异常", e1);
        }
    }
}

UserController中添加一个测试方法:

    @GetMapping("/user/operateLog")
    @OperateLog(title = "测试方法")
    public void testOperateLog(@RequestParam("userId")Integer userId){
        System.out.println(userId);
    }

浏览器访问:http://localhost:8080/user/operateLog?userId=1 查看数据库:

如上:已成功保存,只是有一些小问题,第一个就是乱码问题,我的是因为数据库编码的原因,在datasource的url后追加&characterEncoding=utf8即可解决,其它字段根据实际场景设置,或者不要都行,这只是个演示例子而已。

测试下@AfterThrowing,在UserController中新增的测试方法中制造一个异常:

    @GetMapping("/user/operateLog")
    @OperateLog(title = "测试方法")
    public void testOperateLog(@RequestParam("userId")Integer userId){
        int i = 9/0;
        System.out.println(userId);
    }

浏览器访问:http://localhost:8080/user/operateLog?userId=1 查看数据库:

如上:已将错误记录保存,而且也不乱码了。

 

 

 

你可能感兴趣的:(springboot)