当Controller层,收到页面请求,我们需要记录一下相关日志,一般我们都是在Controller里的每个方法上加上一段:
logger.info("收到请求");
这样不仅增加代码量,还增加重复代码;
如何进行优化呢?Spring AOP切面编程可以帮助我们实现!
一、我们先引入所需的jar包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
二、创建class,使用@Aspect注解,表示这是一个切面,再加上@Component将class加入到Spring容器中;
三、通过@Pointcut注解,选择我们的切入点:
@Pointcut("execution(public * com.example.demo.controller..*.*(..))")
public void anyController() {}
execution(public * com.example.demo.controller..*.*(..))切入Controller层下的所有public方法;
四、通过@before注解,在方法执行前切入,也是打印日志的地方,下面贴出代码:
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
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;
@Aspect
@Component
public class RequestLog {
private final static Logger logger = LoggerFactory.getLogger(RequestLog.class);
@Pointcut("execution(public * com.example.demo.controller..*.*(..))")
public void anyController() {}
/**
* 方法用途:
* 这里用JoinPoint切入点
* ProceedingJoinPoint切入点只能用在@Around注解<br/>
* 操作步骤: TODO<br/>
* ${tags}
*/
@Before("anyController()")
public void before(JoinPoint joinPoint) {
// 请求URL
requestURI();
// 请求参数
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 类路径URL
String methodPackage = methodSignature.getDeclaringTypeName();
// 方法名
String method = methodSignature.getMethod().getName();
StringBuilder builder = new StringBuilder();
builder.append(methodPackage)
.append(".")
.append(method)
.append(",请求参数:")
.append(JSON.toJSONString(args));
logger.info(builder.toString());
}
/**
* 打印请求URL
*/
private void requestURI() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String requestURL = request.getRequestURI();
logger.info(String.format("请求URL:%s", requestURL));
}
}
这样我们就不需要在每个Controller的每个方法里去写打印日志的代码了,还没有结束,如果我们controller方法用了HttpServletRequest,我们的args在转换json就会报错,解决方式很简单,加上try catch捕获异常:
Object[] args = joinPoint.getArgs();
StringBuilder paramBuilder = new StringBuilder();
for (int i = 0; i < args.length; i++) {
Object obj = args[i];
try {
String param = JSON.toJSONString(obj);
paramBuilder.append(param);
} catch (Exception e) {
logger.error("args is HttpServletRequest", e);
}
}
或者我们切入点改成service层或者自定义一个注解,只在没有用到HttpServletRequest的方法上加上自定义注解,我们的切面切入点改成自定义注解:
import com.example.demo.enums.EditTypeEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogByMethod {
String remark();
String editType() default "QUERY";
}
切入点:
@Pointcut(""@annotation(com.example.demo.annotation.LogByMethod)")
public void anyController() {}
在controller方法上加上自定义注解:
@LogByMethod(remark = "查询所有人", editType = "QUERY")
@PostMapping("/queryAll")
public int queryAll(@RequestBody Map<String, String> map) {
threadService.queryAll();
return 200;
}
postman请求:
运行结果:
喜欢的朋友点赞、加关注!