1.在pom文件中添加aop依赖
<!-- aop 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 用于日志切面中,以 json 格式打印出入参-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
2.自定义一个切面类
package com.ruoyi.framework.aspectj;
import com.google.gson.Gson;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* @author lsy
* @version 1.0.0
* @Description TODO
* @createTime 2023年04月08日 09:40:00
*/
@Aspect
@Component
public class MyLogAspect {
private final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);
@Pointcut("execution(public * com.ruoyi.project.study.controller..*.*(..))")
public void myLog() {
}
/**
* 在切点之前织入
*
* @param joinPoint
* @throws Throwable
*/
@Before("myLog()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 打印请求相关参数
logger.info("========================================== Before ==========================================");
// 打印请求 url
logger.info("URL : {}", request.getRequestURL().toString());
// 打印 Http method
logger.info("HTTP Method : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
// 打印请求的 IP
logger.info("IP : {}", request.getRemoteAddr());
// 打印请求入参
logger.info("Request Args : {}", new Gson().toJson(joinPoint.getArgs()));
}
/**
* 环绕
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("myLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
logger.info("进入日志切面方法");
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 打印出参
logger.info("Response Args : {}", new Gson().toJson(result));
// 执行耗时
logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
logger.info("=========================================== End ===========================================");
// 每个请求之间空一行
logger.info("");
return result;
}
}
aspect相关注解:
-
@Aspect:声明该类为一个注解类;
-
@Pointcut:定义一个切点,后面跟随一个表达式,表达式可以定义为某个 package 下的方法,也可以是自定义注解等;切点定义好后,就是围绕这个切点做文章了:
-
@Before: 在切点之前,织入相关代码;
-
@After: 在切点之后,织入相关代码;
-
@AfterReturning: 在切点返回内容后,织入相关代码,一般用于对返回值做些加工处理的场景;
-
@AfterThrowing: 用来处理当织入的代码抛出异常后的逻辑处理;
-
@Around: 在切入点前后织入代码,并且可以自由的控制何时执行切点
执行顺序
@Around注解方法的前半部分业务逻辑
->@Before注解方法的业务逻辑
->目标方法的业务逻辑
->@Around注解方法的后半部分业务逻辑(@Around注解方法内的业务逻辑若对ProceedingJoinPoint.proceed()方法没做捕获异常处理,直接向上抛出异常,则不会执行Around注解方法的后半部分业务逻辑;若做了异常捕获处理,则会执行)。
->@After(不管目标方法有无异常,都会执行@After注解方法的业务逻辑)
->@AfterReturning(若目标方法无异常,执行@AfterReturning注解方法的业务逻辑)
->@AfterThrowing(若目标方法有异常,执行@AfterThrowing注解方法的业务逻辑)
3.写一个测试类
package com.ruoyi.project.study.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lsy
* @version 1.0.0
* @Description TODO
* @createTime 2023年04月08日 09:43:00
*/
@Slf4j
@RestController
@RequestMapping("/api/test")
public class ApiTestController {
@GetMapping("getTest")
public String getTest(Integer id,String username) {
log.info("testGet ...");
return "success";
}
}
4.测试
其它方法如post、delete也可以自行测试
另外,还可以使用自定义注解的方式,可以更灵活的让指定接口输出日志。
- 自定义一个日志注解
package com.ruoyi.framework.aspectj.lang.annotation; import java.lang.annotation.*; @Target({ ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyLog { /** * 接口描述 可以自己根据项目需求自定义多个属性 */ public String title() default ""; }
-
重写上面的切入点,使用注解切入
@Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.MyLog)") public void myLog() { }
-
接口上添加@MyLog(title = "测试注解接口。。。随便写")即可。