前言
平时项目开发过程中,记录操作日志是必不可少的,然而使用传统的方法,即在业务代码中加入记录日志这一部分代码,会导致这项工作很繁琐,而且都是在做一些重复性工作,还增加大量冗余代码,这种方式记录日志肯定是不可行的。
利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。(来自百度百科!)
AOP主要可以用来进行日志记录,性能统计,安全控制,事务处理,异常处理等等。
实现
1、添加Maven依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.20</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
2、编写几个需要用到的类
- 1:日志状态枚举
/** * @author luoweijie * @date 2022/02/25 */ public enum LogStatus { /** * 日志状态枚举 */ SUCCESS(0), FAILED(1); private final Integer status; LogStatus(Integer status) { this.status = status; } }
- 2:操作类型枚举
/** * @author luoweijie * @date 2022/02/25 */ public enum LogType { /** * 日志类型枚举 */ TEST("测试"), OTHER("其他日志"); private final String value; LogType(String value) { this.value = value; } }
- 3:工具类
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Objects; /** * @author luoweijie * @date 2022/02/25 */ public abstract class HttpContextUtils { public static HttpServletRequest getRequest() { return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); } }
- 3:测试实体类
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author luoweijie * @date 2022/02/25 */ @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String username; private String password; }
3、自定义Log注解
注解内容可自行拓展~~
import com.xm.model.enums.LogType; import java.lang.annotation.*; /** * @author luoweijie * @date 2022/02/25 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { LogType type() default LogType.OTHER; boolean saveRequestParams() default false; }
4、编写切面类
import cn.hutool.json.JSONUtil; import com.xm.annotation.Log; import com.xm.model.enums.LogStatus; import com.xm.utils.HttpContextUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.HashMap; import java.util.Map; /** * @author luoweijie * @date 2022/02/25 */ @Aspect @Component public class LogAspect { private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); private Long startTime; @Pointcut("@annotation(com.xm.annotation.Log)") public void pointCut() { } @Before("pointCut()") public void before() { startTime = System.currentTimeMillis(); } @AfterThrowing("pointCut()") public void afterThrowing(JoinPoint joinPoint) { handleLog(joinPoint, LogStatus.FAILED); } @AfterReturning("pointCut()") public void afterReturning(JoinPoint joinPoint) { handleLog(joinPoint, LogStatus.SUCCESS); } @Async public void handleLog(JoinPoint joinPoint, LogStatus logStatus) { // get request HttpServletRequest request = HttpContextUtils.getRequest(); // get execution time(ms) long time = System.currentTimeMillis() - startTime; // get method MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); // get log annotation Log log = method.getDeclaredAnnotation(Log.class); if (log.saveRequestParams()) { // do something.. logger.info("execution time : {}ms", time); logger.info("request url : {}", request.getRequestURL()); logger.info("request params : {}", parseToJson(method.getParameters(), joinPoint.getArgs())); // save to database } } /** * 将参数转为JSON格式 * * @param parameters 参数属性 * @param args 参数值 * @return JSON */ private String parseToJson(Parameter[] parameters, Object[] args) { logger.info(parameters.length + ", " + args.length); StringBuffer sb = new StringBuffer("{ \"params\": "); Map<String, Object> paramsMap = new HashMap<>(parameters.length); for (int i = 0; i < parameters.length; i++) { Parameter parameter = parameters[i]; Object arg = args[i]; logger.info("param type: {}, param name : {}, param value : {}", parameter.getType(), parameter.getName(), arg); paramsMap.put(parameter.getName(), arg); } return sb.append(JSONUtil.toJsonStr(paramsMap)).append("}").toString(); } }
5、写个Controller进行测试
import com.xm.annotation.Log; import com.xm.model.entity.User; import com.xm.model.enums.LogType; import org.springframework.web.bind.annotation.*; import java.util.concurrent.TimeUnit; /** * @author luoweijie * @date 2022/02/25 */ @RestController @RequestMapping("log") public class LogController { @Log(type = LogType.OTHER, saveRequestParams = true) @PostMapping("test") public String testLog(@RequestParam("code") String code, @RequestBody User user) throws InterruptedException { TimeUnit.SECONDS.sleep(2); return "test log annotation success!"; } }
启动应用访问
不出意外可以看到如下信息: