六、集成全局AOP切面,进行访问时间与日志打印
- 2019 年 11 月 7 日
- 筆記
本次开发环境为:
系统:Windows 10 10.0
JDK:JRE: 1.8.0_152-release-1136-b43 amd64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
开发工具:IntelliJ IDEA 2018.1.8
springboot框架:2.2.0
1、在pom.xml中增加包引用
<!-- Springboot的AOP包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2、新建一个controller包用于本次测试使用
@RestController public class HelloWorldAOPController { private Logger log = LoggerFactory.getLogger(HelloWorldAOPController.class); /** * hello请求测试 实例:http://localhost:8081/hello?name=world * @param name 打招呼的姓名 * @return 返回打招呼的整体语句 */ @RequestMapping("/hello") public String hello(String name){ StringBuffer hellos = new StringBuffer(); hellos.append("Hello "); hellos.append(name); return hellos.toString(); } }
3、新建一个WebTimeAspect类,用于记录日志切面,这里需要注意截止记录是在retrun后的切入点,因其是对全局controller进行的切面,因此使用@AfterReturning更合适的一些,如果我们所有均切入则使用@After更合适一些。
@Aspect @Component @Order(1) public class WebTimeAspect { private Logger log = LoggerFactory.getLogger(WebTimeAspect.class); /** * 声明一个线程,用于记录请求与响应整个周期期间在服务端消耗的时间 */ private ThreadLocal<Long> startTime = new ThreadLocal<>(); /** * 在请求响应之前,即请求到达当前服务端 * 所有demo3下的controller均经过该切面 * @param joinPoint */ @Before("within(com.cnhuashao.rapiddevelopment.core..*.*)") public void doBefore(JoinPoint joinPoint){ log.info("----------- WebTimeAspect doBefore -----------------------------------------"); startTime.set(System.currentTimeMillis()); } /** * 在请求响应之后,即请求已经经过controller处理返回后 * @param rvt */ @AfterReturning(value = "within(com.cnhuashao.rapiddevelopment.core..*.*)",returning = "rvt") public void doAfterReturning(Object rvt){ log.info("-----------Start WebTimeAspect doAfterReturning ------"); log.info("本次处理请求耗费时间 : {}",(System.currentTimeMillis() - startTime.get())); log.info("-----------End WebTimeAspect doAfterReturning -------------------------------"); } }
4、进行测试请求响应处理时间
访问地址:http://localhost:8081/hello?name=cnHuaShao

image.png
5、新建一个WebLogAspect类,用于请求日志与响应日志的记录
@Aspect @Component @Order(2) public class WebLogAspect { private Logger log = LoggerFactory.getLogger(WebLogAspect.class); /** * 访问localhost时打印的IP地址 */ private static final String IP_LOCALHOST ="0:0:0:0:0:0:0:1"; /** * com.cnhuashao.rapiddevelopment.core包及所有子包下任何类的任何方法 */ @Pointcut("execution(* com.cnhuashao.rapiddevelopment.core..*.*(..))") public void webLog(){ } /** * 在请求响应之前,即请求到达当前服务端 * 所有demo3下的controller均经过该切面 * @param joinPoint */ @Before("webLog()") public void doBefore(JoinPoint joinPoint){ log.info(" ---------- WebLogAspect doBefore --------------"); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); //在系统启动时拦截器经过该位置时会触发空指针异常,这里需要进行非空判断 if (attributes != null) { HttpServletRequest request = attributes.getRequest(); log.info(" 地址: {}", request.getRequestURL().toString()); log.info(" 请求方式: ", request.getMethod()); String ip = request.getRemoteAddr(); if (!IP_LOCALHOST.equals(ip)){ log.info(" 客户端IP: {}", ip); } log.info(" 请求参数: {}" + Arrays.toString(joinPoint.getArgs())); } } /** * 在请求响应之后,即请求已经经过controller处理返回后 * @param rvt */ @AfterReturning(pointcut = "webLog()",returning = "rvt") public void doAfterReturning(Object rvt){ log.info(" ------------Start WebLogAspect doAfterReturning ------"); log.info(" 响应结果 : {}",rvt.toString()); log.info(" ------------End WebLogAspect doAfterReturning ------"); } /** * 异常切入 * @param error */ @AfterThrowing(pointcut = "webLog()",throwing = "error") public void doAfterThrowing(Throwable error){ log.error(" ------------Start WebLogAspect doAfterThrowing ------"); log.error("请求处理过程中发生异常:{}",error.getMessage()); log.error(" ------------Start WebLogAspect doAfterThrowing ------"); } }
6、进行测试日志切面
访问地址:http://localhost:8081/hello?name=cnHuaShao

image.png
至此已经实现访问时间与日志的切面,里面有一些优化还需要更改一下
1、整合切面使用包
在上述代码中每个方法头均有一个切入扫描的包路径,这样在我们日常使用配置时无法统一化管理,为了解决该问题,特将这种统一的全局值提出来。 更改WebTimeAspect类如下
@Aspect @Component @Order(1) public class WebTimeAspect { private Logger log = LoggerFactory.getLogger(WebTimeAspect.class); /** * 声明一个线程,用于记录请求与响应整个周期期间在服务端消耗的时间 */ private ThreadLocal<Long> startTime = new ThreadLocal<>(); /** * com.cnhuashao.rapiddevelopment.core包及所有子包下任何类的任何方法 */ @Pointcut("execution(* com.cnhuashao.rapiddevelopment.core..*.*(..))") public void webTime(){ } /** * 在请求响应之前,即请求到达当前服务端 * 所有demo3下的controller均经过该切面 * 暂存:@Before("within(com.cnhuashao.rapiddevelopment.core..*.*)") * @param joinPoint */ @Before("webTime()") public void doBefore(JoinPoint joinPoint){ log.info("----------- WebTimeAspect doBefore -----------------------------------------"); startTime.set(System.currentTimeMillis()); } /** * 在请求响应之后,即请求已经经过controller处理返回后 * 暂存:@AfterReturning(value = "within(com.cnhuashao.rapiddevelopment.core..*.*)",returning = "rvt") * @param rvt */ @AfterReturning(pointcut = "webTime()",returning = "rvt") public void doAfterReturning(Object rvt){ log.info("-----------Start WebTimeAspect doAfterReturning ------"); log.info("本次处理请求耗费时间 : {}",(System.currentTimeMillis() - startTime.get())); log.info("-----------End WebTimeAspect doAfterReturning -------------------------------"); } }
2、优先级设计
在上述代码中每个aspect类的顶部都有一个注释@Order,这个注释是标记整个切面类的运行优先级的,我们的@Before与@AfterReturning根据order值执行的顺序是不一样的。 @Before order越小越优先 @AfterReturning order越大越优先
3、日常使用时我们应该将全局相关的切面包与配置包放到base包中,这样有利于全局化的相关信息管理。
代码示例
本文的相关例子可以查看仓库中的RapidDevelopment-demo3
目录: Gitee 地址
本文声明:

88×31.png
知识共享许可协议 本作品由 cn華少 采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。