springboot 如何使用自定义注解+aop实现全局日志实现持久化操作
前言:
一般的平台在开发管理平台的时候都有一个日志管理模块,说到日志,就想到需求。通常优先考虑全局日志,会有操作人、请求方法、请求参数、操作时间、响应结果等字段。针对这样的需求相对来说很简单,就不说了。不知道你们有没有遇到这种需求,显示的时候还需要,用户操作功能模块的名称,或者操作状态不是常规的四种状态,遇到这种需求就比较头疼啦。如果将添加日志功能以接口的形式提供给前端,那么就会出现每个接口调用两次的情况。前端在添加日志记录的传参的时候,还要对每个接口做一些状态的判断的逻辑处理。针对这种情况我们该怎么做呢? 后来想了一下,建议用自定义注解+aop来实现。
设计思路
通过自定义一个注解,将无法直接获取的参数以注解的形式传参。利用AOP获取自定义注解定义的参数,然后根据具体的业务需求,实现业务功能。使用的时候,只需要在接口上贴上自定义的注解,根据实际操作设置属性值。
添加依赖
<!--aop--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.2.6.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.5</version> </dependency>
示例
1.首先自定一个注解,根据业务需求,定义属性。
package com.hc.manager.common.aop.annotation; import java.lang.annotation.*; /** * LogAnnotation * * @author summer.chou * @version V1.0 * @date 2020年3月18日 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LogAnnotation { /** * 模块 */ String title() default ""; /** * 功能 */ String action() default ""; }
2.aop读取自定义注解属性值,再做业务逻辑处理。
package com.hc.manager.common.aop.aspect; import com.alibaba.fastjson.JSON; import com.hc.manager.common.aop.annotation.LogAnnotation; import com.hc.manager.common.utils.HttpContextUtils; import com.hc.manager.common.utils.IPUtils; import com.hc.manager.entity.SysLog; import com.hc.manager.mapper.SysLogMapper; import com.hc.manager.service.HttpSessionService; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; /** * 日志切面 * * @author summer.chou * @version V1.0 * @date 2020年3月18日 */ @Aspect @Component @Slf4j public class SysLogAspect { @Lazy @Resource private SysLogMapper sysLogMapper; @Lazy @Resource private HttpSessionService httpSessionService; /** * 此处的切点是注解的方式 * 只要出现 @LogAnnotation注解都会进入 */ @Pointcut("@annotation(com.hc.manager.common.aop.annotation.LogAnnotation)") public void logPointCut() { } /** * 环绕增强,相当于MethodInterceptor */ @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //执行方法 Object result = point.proceed(); //执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; //保存日志 try { saveSysLog(point, time); } catch (Exception e) { log.error("sysLog,exception:{}", e, e); } return result; } /** * 把日志保存 */ private void saveSysLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysLog sysLog = new SysLog(); LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); if (logAnnotation != null) { //注解上的描述 sysLog.setOperation(logAnnotation.title() + "-" + logAnnotation.action()); } //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); sysLog.setMethod(className + "." + methodName + "()"); log.info("请求{}.{}耗时{}毫秒", className, methodName, time); try { //请求的参数 Object[] args = joinPoint.getArgs(); String params = null; if (args.length != 0) { params = JSON.toJSONString(args); } sysLog.setParams(params); } catch (Exception e) { log.error("sysLog,exception:{}", e, e); } //获取request HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); //设置IP地址 sysLog.setIp(IPUtils.getIpAddr(request)); log.info("Ip{},接口地址{},请求方式{},入参:{}", sysLog.getIp(), request.getRequestURL(), request.getMethod(), sysLog.getParams()); //用户名 String userId = httpSessionService.getCurrentUserId(); String username = httpSessionService.getCurrentUsername(); sysLog.setUsername(username); sysLog.setUserId(userId); sysLog.setTime((int) time); log.info(sysLog.toString()); sysLogMapper.insert(sysLog); } }