前面的章节我们学习到 Spring Boot Log 日志使用教程Spring Boot 异常处理与全局异常处理,本章我们结合 Aop 面向切面编程来实现全局拦截异常并记录日志。

在 Spring Boot 中 Aop 与 Ioc 可以说是 Spring 的灵魂,其功能也是非常强大。

本项目源码下载

1 新建 Spring Boot 项目

1)File > New > Project,如下图选择 Spring Initializr 然后点击 【Next】下一步

2)填写 GroupId(包名)、Artifact(项目名) 即可。点击 下一步

groupId=com.fishpro

artifactId=aoplog

3)选择依赖 Spring Web Starter 前面打钩。

4)项目名设置为 spring-boot-study-aoplog

2 Pom 中引入 aop 依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <!--aop-->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-aop</artifactId>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-test</artifactId>
  14. <scope>test</scope>
  15. </dependency>
  16. </dependencies>

3 编写处理代码

3.1 程序原理及流程

在 Spring Boot 应用程序中使用 @Aspect 注解来实现 aop ,非常的简单。本示例代码主要实现

本代码供新增了4个文件,文件清单如下,斜杠前是包名,需要新建

  • annotation/Log.java 自定义日志拦截注解
  • aspect/LogAspect.java 自定义日志拦截注解触发条件、环绕增强(在请求前和请求后的执行逻辑)
  • aspect/WebLogAspect.java 基于 Web 的日志拦截,定义了 Web 的请求前动作、请求后动作、请求生命周期中的动作
  • controller/IndexController.java 控制层 Controller 类,方便测试,定了 /log

本示例使用到的新的注解包括

  • @Aspect 面向切面编程注解,通常应用在类上
  • @Pointcut Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码
  • @Around:环绕增强,相当于MethodInterceptor
  • @AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
  • @Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
  • @AfterThrowing:异常抛出增强,相当于ThrowsAdvice
  • @After: final增强,不管是抛出异常或者正常退出都会执行

流程说明

如图所示,用户访问 IndexController/log 路由的时候,被 WebLogAspect 拦截到了带有 @log 注解方法的信息,根据 WebLogAspect 中的定义处理了请求前 请求后 请求中的信息 并存于日志中。

3.2 自定义日志注解 @log

本文不打算详细说明什么是自定义注解,如何使用,可以知道的是,

  • 注解是一种元数据形式。即注解是属于java的一种数据类型,和类、接口、数组、枚举类似
  • 注解用来修饰,类、方法、变量、参数、包。
  • 注解不会对所修饰的代码产生直接的影响。

定义注解 @Log,在 anotation 包下 Log.java

  1. /**
  2. * 使用@interface将定义一个注解 这里是log
  3. * 用于日志aop编程
  4. * */
  5. @Target(ElementType.METHOD)
  6. @Retention(RetentionPolicy.RUNTIME)
  7. public @interface Log {
  8. String value() default "";
  9. }

如何使用,我没有在 IndexController 的方法中使用 @Log 注解

3.3 定义测试用 IndexController

  1. @Controller
  2. public class IndexController {
  3. @Log("日志注解,配合WebAspect记录请求前、请求后、请求过程")
  4. @RequestMapping("/log")
  5. @ResponseBody
  6. public String log(String name){
  7. return "log";
  8. }
  9. }

3.4 定义 aop 类 LogAspect 与 WebLogAspect

  1. @Aspect
  2. @Component
  3. public class LogAspect {
  4. private static final Logger logger=LoggerFactory.getLogger(LogAspect.class);
  5. /**
  6. * 这里指定使用 @annotation 指定com.fishpro.aoplog.annotation.Log log注解
  7. * */
  8. @Pointcut("@annotation(com.fishpro.aoplog.annotation.Log)")
  9. public void logPointCut(){
  10. }
  11. public Object around(ProceedingJoinPoint point) throws Throwable{
  12. long beginTime = System.currentTimeMillis();
  13. // 执行方法
  14. Object result = point.proceed();
  15. // 执行时长(毫秒)
  16. long time = System.currentTimeMillis() - beginTime;
  17. //异步保存日志 这里是文本日志
  18. return result;
  19. }
  20. void saveLog(ProceedingJoinPoint joinPoint, long time) throws InterruptedException{
  21. }
  22. }
  1. @Aspect
  2. @Component
  3. public class WebLogAspect {
  4. private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
  5. /**
  6. * 指定 controller 包下的注解
  7. * */
  8. @Pointcut("execution( * com.fishpro.aoplog.controller.*.*(..))")//两个..代表所有子目录,最后括号里的两个..代表所有参数
  9. public void logPointCut() {
  10. }
  11. /**
  12. * 指定当前执行方法在logPointCut之前执行
  13. * */
  14. @Before("logPointCut()")
  15. public void doBefore(JoinPoint joinPoint) throws Throwable{
  16. // 接收到请求,记录请求内容
  17. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  18. HttpServletRequest request = attributes.getRequest();
  19. // 记录下请求内容
  20. logger.info("请求地址 : " + request.getRequestURL().toString());
  21. logger.info("HTTP METHOD : " + request.getMethod());
  22. // 获取真实的ip地址
  23. //logger.info("IP : " + IPAddressUtil.getClientIpAddress(request));
  24. logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
  25. + joinPoint.getSignature().getName());
  26. logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
  27. //loggger.info("参数 : " + joinPoint.getArgs());
  28. }
  29. /**
  30. * 指定在方法之后返回
  31. * */
  32. @AfterReturning(returning = "ret", pointcut = "logPointCut()")// returning的值和doAfterReturning的参数名一致
  33. public void doAfterReturning(Object ret) throws Throwable {
  34. // 处理完请求,返回内容(返回值太复杂时,打印的是物理存储空间的地址)
  35. logger.info("返回值 : " + ret);
  36. }
  37. @Around("logPointCut()")
  38. public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
  39. long startTime = System.currentTimeMillis();
  40. Object ob = pjp.proceed();// ob 为方法的返回值
  41. logger.info("耗时 : " + (System.currentTimeMillis() - startTime));
  42. return ob;
  43. }
  44. }

3.5 测试

在浏览器输入,注意端口在 application.yml 中已经改为了8085

http://localhost:8085/log?name=fishpro

在控制台输出

  1. 2019-07-13 17:12:15.388 INFO 10055 --- [nio-8085-exec-1] com.fishpro.aoplog.aspect.WebLogAspect : 请求地址 : http://localhost:8085/log
  2. 2019-07-13 17:12:15.388 INFO 10055 --- [nio-8085-exec-1] com.fishpro.aoplog.aspect.WebLogAspect : HTTP METHOD : GET
  3. 2019-07-13 17:12:15.389 INFO 10055 --- [nio-8085-exec-1] com.fishpro.aoplog.aspect.WebLogAspect : CLASS_METHOD : com.fishpro.aoplog.controller.IndexController.log
  4. 2019-07-13 17:12:15.389 INFO 10055 --- [nio-8085-exec-1] com.fishpro.aoplog.aspect.WebLogAspect : 参数 : [fishpro]
  5. 2019-07-13 17:12:17.219 INFO 10055 --- [nio-8085-exec-1] com.fishpro.aoplog.aspect.WebLogAspect : 耗时 : 4934
  6. 2019-07-13 17:12:17.997 INFO 10055 --- [nio-8085-exec-1] com.fishpro.aoplog.aspect.WebLogAspect : 返回值 : log

本项目源码下载


关联阅读:

Spring Boot Log 日志使用教程

Spring Boot 全局异常处理


欢迎关注我的微信公众号,我们一起编程聊天看世界

Spring Boot 使用 Aop 实现日志全局拦截的更多相关文章

  1. spring boot集成aop实现日志记录

    1.pom依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  2. Spring Boot使用AOP的正确姿势

    一.为什么需要面向切面编程? 面向对象编程(OOP)的好处是显而易见的,缺点也同样明显.当需要为多个不具有继承关系的对象添加一个公共的方法的时候,例如日志记录.性能监控等,如果采用面向对象编程的方法, ...

  3. Spring Boot系列一:默认日志logback配置解析

    前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候,是带着下面几个问题来查资料的,你呢 如何引入日志? 日志输出格式以及输出方式如何配置? 代码中如何使用? 正文 Sp ...

  4. Spring Boot学习——AOP编程的简单实现

    首先应该明白一点,AOP是一种编程范式,是一种程序设计思想,与具体的计算机编程语言无关,所以不止是Java,像.Net等其他编程语言也有AOP的实现方式.AOP的思想理念就是将通用逻辑从业务逻辑中分离 ...

  5. spring boot 中AOP的使用

    一.AOP统一处理请求日志 也谈AOP 1.AOP是一种编程范式 2.与语言无关,是一种程序设计思想 面向切面(AOP)Aspect Oriented Programming 面向对象(OOP)Obj ...

  6. spring boot使用slf4j输出日志

    spring boot使用slf4j输出日志 https://blog.csdn.net/qq442270636/article/details/79406346 Spring Boot SLF4J日 ...

  7. Spring Boot Logback几种日志详解

    日志对于应用程序来说是非常重要的,Spring框架本身集成了不少其他工具,我们自身的应用也会使用到第三方库,所以我们推荐在Spring应用中使用SLF4J/Logback来记录日志. SLF4J与Lo ...

  8. Spring Boot中使用logback日志框架

    说明:Spring Boot在最新的版本中默认使用了logback框架.一般来说使用时只需在classpath下创建logback.xml即可,而官方推荐使用logback-spring.xml替代, ...

  9. spring boot使用AOP切面编程

    spring boot使用AOP 1.在pom文件中添加依赖: <!--spring boot aop切面--> <dependency> <groupId>org ...

随机推荐

  1. JQuery/JS插件 jsTree加载树,普通加载,点一级加载一级

    前端: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <tit ...

  2. beego orm 多对多插入和查询操作

    // User 用户表 type User struct { ID int UserName string Password string Articles []*Article `orm:" ...

  3. SSG (slow global), TTG (typical global) and FFG (fast global)

    https://semiwiki.com/x-subscriber/clk-design-automation/4481-variation-alphabet-soup/ n response, fo ...

  4. 十大常见web漏洞及防范

    十大常见web漏洞 一.SQL注入漏洞 SQL注入攻击(SQL Injection),简称注入攻击.SQL注入,被广泛用于非法获取网站控制权,是发生在应用程序的数据库层上的安全漏洞.在设计程序,忽略了 ...

  5. js获取用户当前地理位置(省、市、经纬度)

    在很多情况下,我们需要用到定位功能,来获取用户当前位置.当前比较流行的定位API有腾讯地图.百度地图.高德地图.搜狗地图等等,在这里我使用的是腾讯地图定位API,根据用户IP获取用户当前位置,API返 ...

  6. 【经典数据结构】B树与B+树的解释

    本文转载自:http://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html 前面讲解了平衡查找树中的2-3树以及其实现红 ...

  7. java多线程信息共享

    上篇文章知识介绍了多线程的创建和启动问题,各个子线程和子线程或者说子线程和main线程没有信息的交流,这篇文章主要探讨线程之间信息共享以及交换问题.这篇文章主要以一个卖票例子来展开. 继承Thread ...

  8. [Jenkins] Jenkins的启动停止并修改默认端口

    在Win系统下面,经常使用Jenkins今天自动化测试工作,但是在搭建的时候还是有些坑 1.选择性安装: 一般会选择windows,会下载一个压缩包,然后step by step就可以安装成功,这个方 ...

  9. 吴裕雄 python 机器学习——数据预处理嵌入式特征选择

    import numpy as np import matplotlib.pyplot as plt from sklearn.svm import LinearSVC from sklearn.li ...

  10. StatelessWidget 无状态组件 StatefulWidget 有状态组件 页面上绑定数据、改变页面数据

    一.Flutter 中自定义有状态组件 在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget. StatelessWidget ...