封面作者:yemoze1314
一、问题
1.1、环境
电脑环境:Windows 10;
开发工具:IntelliJ IDEA;
数据库环境:Redis 3.2.100
JDK环境: Jdk1.8;
1.2、问题
因为我想看每个请求过来的入参和出参,所以结合 SpringBoot 的AOP(面向切面编程)写了一个日志打印的方法;那么具体是如何实现的呢?
二、解答
1.jar包依赖,下面这3个依赖包是切面功能实现的必要条件;
<!-- 面向切面 added by Taozhouchuan at 2020-8-3 19:26:53 :http://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<!-- http://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!-- http://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.2</version>
</dependency>
2.AOP的point Cut切面的切点
private final String POINT_CUT = "execution(public * com.demo.controller.*.*(..))";
3.Spring 的AOP切点主要有5种:
package com.demo.filter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* 拦截器
* @author tzc
*/
@Aspect
@Component
@Slf4j
public class LogIntercept {
ThreadLocal<Long> startTime = new ThreadLocal<Long>();
private final String POINT_CUT = "execution(public * com.demo.controller.*.*(..))";
/**
* 切点表达式中,..两个点表明多个,*代表一个, 上面表达式代表切入com.demo.controller包下的所有类的所有方法,
* 方法参数不限,返回类型不限。 其中访问修饰符可以不写,不能用*,,第一个*代表返回类型不限,第二个*表示所有类,第三个*表示所有方法,
* ..两个点表示方法里的参数不限。 然后用@Pointcut切点注解,想在一个空方法上面,
*/
/**
* 指定切面,这里可以是一个空方法;
*/
@Pointcut(POINT_CUT)
private void pointCut() {
}
//请求method前打印内容
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint) {
startTime.set(System.currentTimeMillis());
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//打印请求内容
log.error("=================[Before]请求内容===============");
log.info("==================[Before]请求地址:{}", request.getRequestURL().toString());
log.info("==================[Before]请求方式:{}", request.getMethod());
log.info("==================[Before]请求类方法:{}", joinPoint.getSignature());
log.info("==================[Before]请求类方法参数:{}", Arrays.toString(joinPoint.getArgs()));
log.info("==================[Before]请求内容===============");
}
//环绕执行:定义需要匹配的切点表达式,同时需要匹配参数
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
log.info("==================[Around]方法环绕前置start=====================>");
//这句必须有 往下执行方法
Object result = pjp.proceed();
log.info("==================[Around]方法环绕后置start=====================>");
log.info("==================[Around]耗时(毫秒):{}", (System.currentTimeMillis() - startTime.get()));
log.info("==================[Around]返回数据:{}", result);
log.info("==================[Around]方法环绕end======================>");
return result;
}
//方法执行后
@After(value = "pointCut()")
public void after(){
log.debug("==================[After]切点方法执行后");
}
//方法异常拦截
@AfterThrowing(value = "pointCut()")
public void afterThrowing(){
log.debug("==================[AfterThrowing]切点方法抛出异常");
}
//在方法执行完结后打印返回内容
@AfterReturning(returning = "o", pointcut = "pointCut()")
public void afterReturning(Object o) {
log.info("==================[AfterReturning]Response内容:" + JSON.toJSONString(o));
log.info("==================[AfterReturning]请求消耗时间:{}ms", (System.currentTimeMillis() - startTime.get()));
}
}
这样,一个从0到功能实现的日志打印的拦截器就完成了;控制台打印日志如下:
完毕~
三、总结
如果你照着上面的方法配置,如果还是没有被拦截(因为没有拦截不会报错),所以请着重从上面3个角度排查:
1、jar包是否在pom.xml文件中正确引用,或者library包下载正确,并关联到项目;
2、pointCut 的execute 的拦截的路径是否正确,这里着重查看;
3、java代码有没有bug;