写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!
本篇记录一下项目中使用自定义注解实现日志记录,对自定义注解的原理大家感兴趣的可以自行百度,先对本篇涉及的几个注解简单介绍下:
@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 查看数据库:
如上:已将错误记录保存,而且也不乱码了。