Springboot2 配置AOP日志的方法步骤

 更新时间:2020年06月22日 10:20:42   作者:长草颜团子-张晓祥  
这篇文章主要介绍了Springboot2 配置AOP日志的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

Java技术迷

Spring boot2 配置AOP前置增强,后置增强,异常增强,环绕增强,最终增强

关于AOP切面相关具体概念不做过多阐述(概念弄懂有利于理解思想),这是配置AOP的各种增强日志,解决日志嵌套在业务代码的麻烦和不科学

先来个Git demo项目压压惊: https://github.com/zhang-xiao-xiang/boot-aop  (有的更新了一些)

1pom依赖(这里使用log4j2作为日志框架,因为比log4j或者其他日志框架,它效率更高,功能更加强大)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- 引入log4j2依赖(注意还有排除依赖) -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 排除exclude掉spring-boot的默认log框架配置 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <!-- 加上这个才能辨认到log4j2.yml文件(如果日志要输出到本地或者具体磁盘,需要配置yml) -->
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<!--AOP的支持依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2编写切面类 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.example.nba.aop;//改成你的包名即可
  
import java.util.Arrays;
  
  
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.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;
  
/**
 * AopLog
 *
 * @author 10905 2019/1/4
 * @version 1.0
 */
@Aspect
@Component
public class AopLog {
  //使用org.slf4j.Logger,这是Spring实现日志的方法
  private final static Logger logger = LoggerFactory.getLogger(AopLog.class);
  
  //    切入点注解的表达式:就是需要AOP的地方(一般是业务逻辑层service,当然服务接口调用层controller也行,两者一起打印日志也行
  //    这个类似正则表达式,可以控制日志的精度(包下,类下,方法下)和切面的类型(业务层面,服务接口层面)相当灵活)
    @Pointcut("execution(* com.example.nba.controller.PlayerApi.*(..))")
  // @Pointcut("execution(* com.example.nba.repository.PlayerRep.*(..))")
  //切入点签名的方法,注意返回值必须是void,相当于切入点的无参构造
  public void mypointcut() {
  }
  
  //    前置增强
  @Before("mypointcut()")
  public void Mybefore(JoinPoint jp) {
    logger.info("*前置增强*调用了【" + jp.getTarget().getClass().getSimpleName() +
        "】的【" + jp.getSignature().getName() + "】的方法,方法入参为【"
        + Arrays.toString(jp.getArgs()) + "】");
    // 接收到请求,记录请求内容(这里同样可以在前置增强配置请求的相关信息)
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    logger.info("请求的地址URL : " + request.getRequestURL().toString());
    logger.info("请求的方式HTTP_METHOD : " + request.getMethod());
    logger.info("请求的IP : " + request.getRemoteAddr());
    logger.info("请求的全类名 : " + jp.getSignature().getDeclaringTypeName() + "." + jp.getSignature().getName());
    logger.info("请求的参数(数组形式) : " + Arrays.toString(jp.getArgs()));
  
  }
  
  //后置增强
  @AfterReturning(pointcut = "mypointcut()", returning = "result")
  public void MyafterReturing(JoinPoint jp, Object result) {
    logger.info("*后置增强*调用了【" + jp.getTarget().getClass().getSimpleName() +
        "】的【" + jp.getSignature().getName() + "】的方法,方法返回值【" + result + "】");
  }
  
  //    异常抛出增强
  @AfterThrowing(pointcut = "mypointcut()", throwing = "e")
  public void afterThrowing(JoinPoint jp, RuntimeException e) {
    logger.error("*异常增强*【" + jp.getSignature().getName().getClass().getSimpleName() + "】方法发生异常【" + e + "】");
  }
  
  //    最终增强
  @After("mypointcut()")
  public void afterLogger(JoinPoint jp) {
    logger.info("*最终增强*【" + jp.getSignature().getName() + "】方法结束执行。");
  }
  
  //环绕增强
  @Around("mypointcut()")
  public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
    logger.info("在==>>" + jp.getTarget().getClass().getName() + "类里面使用AOP环绕增强==");
    logger.info("*环绕增强*调用【" + jp.getTarget().getClass().getSimpleName() + "】的【 " + jp.getSignature().getName()
        + "】方法。方法入参【" + Arrays.toString(jp.getArgs()) + "】");
    try {
      Object result = jp.proceed();
      logger.info("*环绕增强*调用 " + jp.getTarget() + "的【 "
          + jp.getSignature().getName() + "】方法。方法返回值【" + result + "】");
      return result;
    } catch (Throwable e) {
      logger.error(jp.getSignature().getName() + " 方法发生异常【" + e + "】");
      throw e;
    } finally {
      logger.info("*环绕增强*执行finally【" + jp.getSignature().getName() + "】方法结束执行<<==。");
    }
  }
}

3测试(数据库代码和业务层等代码就不贴上去了,主要参考AOP的pom依赖和切面类),比如我浏览器请求

http://localhost:8080/player/findAll

控制器显示:

json格式的aop配置(注意文件格式入参需要判断一下(因为excel,PDF,png等格式无法转json)),假如现在的需求是请求和返回参数均以json格式为主,并且记录日志到数据库(排除查询日志的接口,因为查询日志不需要记录到数据库,这里涉及到AOP排除某个接口或者说是某个切面,具体见下)

日志实体(表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.xinzuo.lvyou.pojo;
  
import java.io.Serializable;
import java.util.Date;
  
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
  
/**
 * <p>
 * 日志记录监控表
 * </p>
 *
 * @author zhangxiaoxiang
 * @since 2019-07-08
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class LogMonitor implements Serializable {
  
  private static final long serialVersionUID = 1L;
  
  /**
   * 日志主键ID
   */
  @TableId
  private String logId;
  
  /**
   * 请求IP地址
   */
  private String requestIp;
  
  /**
   * 操作接口反馈描述
   */
  private String summarize;
  /**
   * 响应状态吗
   */
  private Integer responseCode;
  
  /**
   * 请求方式
   */
  private String requestType;
  
  /**
   * 请求接口
   */
  private String requestApi;
  
  /**
   * 请求参数
   */
  private String requestPara;
  
  /**
   * 返回参数
   */
  private String responsePara;
  
  /**
   * 请求到响应处理时间(毫秒)
   */
  private Integer responseTime;
  
  /**
   * 创建时间(请求发出的时间)
   */
  private Date createTime;
  
  /**
   * 日志所属(0移动前端日志,1PC端日志)
   */
  private Integer belongTo;
}

下面是json格式并带有记录到数据库的需求的,假如返回的格式是三段式json,如下

1
2
3
4
5
6
7
8
9
{
  "code": 200,
  "msg": "查询打卡次数成功",
  "data": {
    "photoSave": null,
    "user": null,
    "amount": 27
  }
}

切面类的编写如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package com.xinzuo.lvyou.aop;
  
import com.alibaba.fastjson.JSONArray;
import com.gexin.fastjson.JSON;
import com.gexin.fastjson.JSONObject;
import com.xinzuo.lvyou.dao.LogMonitorDao;
import com.xinzuo.lvyou.pojo.LogMonitor;
import com.xinzuo.lvyou.util.KeyUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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;
import java.util.Date;
  
/**
 * AopLog:日志切面类
 *
 * @author zhangxiaoxiang
 * @date: 2019/05/22
 */
@Component
@Aspect
public class AopJsonLog {
  /**
   * 记录日志到数据库
   */
  @Autowired
  private LogMonitorDao logMonitorDao;
  //记录到数据库
  LogMonitor logMonitor = new LogMonitor();
  long start = 0;
  
  /**
   * 使用org.slf4j.Logger,这是Spring实现日志的方法
   */
  private final static Logger logger = LoggerFactory.getLogger(AopJsonLog.class);
  
  @Pointcut("execution(* com.xinzuo.lvyou.admin..*(..)) ")
  public void myPointcut() {
  }
  
  /**
   * 前置增强 单独配置的前端接口切面
   * 日志打印排除在外,查询日志接口就不记录数据库
   * 排除某个方法使用!execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))
   *
   * @param jp
   */
  @Before("execution(* com.xinzuo.lvyou.controller..*(..)) && !execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))")
  public void MyBefore(JoinPoint jp) {
    logger.info("---------------------------------前端请求接口日志----------------------------------------------");
    // 接收到请求,记录请求内容(这里同样可以在前置增强配置请求的相关信息)
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    logger.info("访问的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
    try {
      logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
    } catch (Exception e) {
      logger.info("请求入参为:图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
      //由于知道这里异常的原因是json转换参数异常,所以就不打印了,不捕获,以免控制台难看或者日志难看
      //e.printStackTrace();
    }
    //logger.info("请求的地址URL:      " + request.getRequestURL().toString());
    //logger.info("请求的方式HTTP_METHOD:  " + request.getMethod());
    //logger.info("请求的IP:        " + request.getRemoteAddr());
  
    start = System.currentTimeMillis();
    logMonitor.setLogId(KeyUtil.genUniqueKey());
    logMonitor.setRequestType(request.getMethod());
    logMonitor.setRequestApi(jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
    try {
      logMonitor.setRequestPara(new JSONArray(Arrays.asList(jp.getArgs())).toString());
    } catch (Exception e) {
      logMonitor.setRequestPara("请求入参为:图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
    }
    logMonitor.setRequestIp(request.getRemoteAddr());
    logMonitor.setCreateTime(new Date());
  
  
  }
  
  /**
   * 后置增强
   * 排除某个方法使用!execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))
   * 日志打印排除在外,查询日志接口就不记录数据库
   * @param jp
   * @param vo
   */
  @AfterReturning(pointcut = "execution(* com.xinzuo.lvyou.controller..*(..)) && !execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))", returning = "vo")
  public void MyafterReturing(JoinPoint jp, Object vo) {
  
    //logger.info("访问的接口: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
    //入参打印json数组格式
//    try {
//      logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
//    } catch (Exception e) {
//      logger.info("请求入参为图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
//      //由于知道这里异常的原因是json转换参数异常,所以就不打印了,不捕获,以免控制台难看或者日志难看
//      //e.printStackTrace();
//    }
    logger.info("方法返回值:" + JSON.toJSONString(vo));
    logMonitor.setResponsePara(JSON.toJSONString(vo));
    long end = System.currentTimeMillis();
    //解析json
    JSONObject json= null;
    try {
      json = JSON.parseObject(JSON.toJSONString(vo));
      logMonitor.setResponseCode(Integer.valueOf(json.get("code").toString()));
      logMonitor.setSummarize(json.get("msg").toString());
    } catch (Exception e) {
      logMonitor.setSummarize("进行图片,视频,excel,PDF等格式操作(由于这个操作特殊一点,所以后台直接把返回状态码默认为200,以实际为准)");
      logMonitor.setResponseCode(200);
      //e.printStackTrace();
    }
  
  
    logMonitor.setBelongTo(0);
    logMonitor.setResponseTime((int) (end - start));
    try {
      logMonitorDao.insert(logMonitor);
    } catch (Exception e) {
      logger.info("AOP系统故障!");
    }
  }
  
//  /**
//   * 异常抛出增强
//   *
//   * @param jp
//   * @param e
//   */
//  @AfterThrowing(pointcut = "myPointcut()", throwing = "e")
//  public void afterThrowing(JoinPoint jp, RuntimeException e) {
//    logger.error("异常增强:" + jp.getSignature().getName().getClass().getSimpleName() + "方法发生异常【" + e + "】");
//  }
  
  /**
   * 环绕增强 :测试的时候finally的切面日志注释不打印,因为日志多了反而不好调试,上线时再取消注释
   *
   * @param jp
   * @return
   * @throws Throwable
   */
  @Around("myPointcut()")
  public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
  
    logger.info("admin---------------------------------后台请求接口日志----------------------------------------------");
    logger.info("访问的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
    //入参打印json数组格式
  
    try {
      logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
    } catch (Exception e) {
      logger.info("请求入参为:图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
      //由于知道这里异常的原因是json转换参数异常,所以就不打印了,不捕获,以免控制台难看或者日志难看
      //e.printStackTrace();
    }
  
    try {
      Object result = jp.proceed();
      logger.info("方法返回值:" + JSON.toJSONString(result));
      return result;
    } catch (Throwable e) {
      logger.info("访问的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
      logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
      logger.error(jp.getSignature().getName() + " 方法发生异常【" + e + "】");
      throw e;
    } finally {
      //logger.info("访问的接口: " + jp.getTarget().getClass().getName() + "."+jp.getSignature().getName());
      //logger.info("请求入参为: "+ new JSONArray(Arrays.asList(jp.getArgs())).toString());
      //logger.info("执行   :" + jp.getSignature().getName() + "方法结束。");
    }
  }
}

------------------------------------下面是spring AOP配置需要的额外依赖----------------------------------------- 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
  <groupId>aopalliance</groupId>
  <artifactId>aopalliance</artifactId>
  <version>1.0</version>
</dependency
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.6.12</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.6.12</version>
</dependency>
<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>2.2</version>
</dependency>

到此这篇关于Springboot2 配置AOP日志的方法步骤的文章就介绍到这了,更多相关Springboot2 配置AOP日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://blog.csdn.net/wozniakzhang/article/details/85784029

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • Java实例讲解动态代理

    Java实例讲解动态代理

    动态代理指的是,代理类和目标类的关系在程序运行的时候确定的,客户通过代理类来调用目标对象的方法,是在程序运行时根据需要动态的创建目标类的代理对象。本文将通过案例详细讲解一下动态代理,需要的可以参考一下
    2022-06-06
  • Spring MVC下 bootStrap服务器分页代码

    Spring MVC下 bootStrap服务器分页代码

    因为Spring 对于ajax直接返回对象,到了WEB页面就转换成json 所以不需要使用JSON转换封装可以直接使用。接下来通过本文给大家分享Spring MVC下 bootStrap服务器分页代码,需要的的朋友参考下
    2017-03-03
  • Mybatis-Plus时间范围查询方式详解

    Mybatis-Plus时间范围查询方式详解

    这篇文章主要介绍了Mybatis-Plus时间范围查询方式详解,通过两种方式结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • 手把手教你实现idea中配置国内源

    手把手教你实现idea中配置国内源

    idea的国内源配置十分重要,能够提升程序开发的效率而且也是减少bug的一种有效防范,本文就来介绍一下idea中配置国内源,具有一定的参考价值,感兴趣的可以了解一下
    2023-07-07
  • IDEA之如何快速生成get和set方法

    IDEA之如何快速生成get和set方法

    这篇文章主要介绍了IDEA之如何快速生成get和set方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • 通过实例解析java String不可变性

    通过实例解析java String不可变性

    这篇文章主要介绍了通过实例解析java String不可变性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Spring Security OAuth2 实现登录互踢的示例代码

    Spring Security OAuth2 实现登录互踢的示例代码

    这篇文章主要介绍了Spring Security OAuth2实现登录互踢的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • 深入浅析Java反射机制

    深入浅析Java反射机制

    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制
    2015-11-11
  • Java集合之同步容器详解

    Java集合之同步容器详解

    这篇文章主要为大家详细介绍了Java集合之同步容器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • springmvc配置线程池Executor做多线程并发操作的代码实例

    springmvc配置线程池Executor做多线程并发操作的代码实例

    今天小编就为大家分享一篇关于springmvc配置线程池Executor做多线程并发操作的代码实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03

最新评论