运用Spring Aop,一个注解实现日志记录

1. 介绍

我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到的。在日常项目中,我们也会经常使用IOC控制反转,但是却感觉AOP很少会运用到。其实AOP大有用处,甚至可以让你偷偷懒。

举一个例子,假如现在要让你记录每一个请求的请求IP,请求的方法,请求路径,请求的参数,返回参数,你会怎么做?你会想,那简单啊,我直接 log.info("xxxx") 输出日志不行吗,简单!可是你要想清楚,每个请求请求的方法不一定是同一个,有一些请求可能请求编辑方法,另外一些请求可能请求登录方法,这么多方法,你每一个方法下面都重复写了差不多6,7行重复代码,你觉得这好吗?

这里,如果我们使用Aop来记录日志,那是再好不过了。我们可以看看一个方法的执行过程来理解AOP。

下面再来看使用AOP后的执行过程。

AOP是面向切面编程,面向切面思想就是让我们把程序想象成一条一条管道连接起来的大管道,而AOP就是在管道和管道之间的过滤网,能够在不影响管道的情况下对管道中传输的数据进行记录,修改。

使用AOP我们可以很方便地进行操作日志记录,性能日志记录,请求日志记录,事务操作,安全管理等。这么说可能很抽象,再详细点说就是各种日志记录我们可以利用AOP来进行记录,而不用在业务逻辑代码中插入,安全管理就是我们可以在请求进来前对请求中的数据进行解密,在请求返回的时候对数据进行加密。这么说AOP很像Java里面的拦截器,过滤器和监听器的结合。

具体AOP的原理就不细讲了,那是另外一篇文章了,有关于动态代理。

2. 实践

说了这么多,大白话就是AOP能让我们在不影响原代码的基础上,对代码功能进行添加,修改

在实现日志记录功能前,我们要先复习一下 Spring Aop 里面的通知顺序(连接点,通知还不知道是什么的,先去B站看一下Spring初级教程)。

先把Aop的starter依赖添加进pom文件中。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.1 定义注解

那现在我们来自定义一个注解,目的是标注该注解的方法将会记录调用该方法的请求信息

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface MyLog {
String value() default "";
}

注解不是本篇重点,有兴趣的童鞋可以搜一下。

2.2 切面类

定义我们的日志记录切面类,切面类中记录请求的信息。

@Component
@Aspect
@Slf4j
public class LogAspect { //切入点为自定义注解
@Pointcut("@annotation(com.example.springaopdemo.demo2.MyLog)")
public void MyLog(){} @Before("MyLog()")
public void Before(JoinPoint jp){
//获取HttpServletRequest对象
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert requestAttributes != null;
HttpServletRequest request = requestAttributes.getRequest();
log.info("==========请求信息==========");
log.info("请求链接 : {}",request.getRequestURL().toString());
log.info("Http Method : {}",request.getMethod());
log.info("Class Method : {}.{}",jp.getSignature().getDeclaringTypeName(),jp.getSignature().getName());
log.info("Ip : {}",request.getRemoteAddr());
log.info("Args : {}", Arrays.asList(jp.getArgs()));
} @Around("MyLog()")
public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
log.info("执行时间 : {} ms", System.currentTimeMillis() - startTime);
log.info("返回参数 : {}", result);
return result;
}
}

通过 @Around 环绕通知我们可以进行简单的性能记录,如果加上 Oshi 我们甚至可以记录执行该方法前后的CPU,内存占用率。

Oshi是Java的免费基于JNA的操作系统和硬件信息库,Github地址是:https://github.com/oshi/oshi

它的优点是不需要安装任何其他本机库,并且旨在提供一种跨平台的实现来检索系统信息,例如操作系统版本,进程,内存和CPU使用率,磁盘和分区,设备,传感器等。

2.3 编写测试方法

编写一个简单的请求,请求需要一个User对象的请求体,返回一个Map结果。

@RestController
@Slf4j
public class Controller { @PostMapping("/test")
@MyLog
public Map<String, Object> testAop(@RequestBody User user){
Map<String,Object> map = new HashMap<>();
map.put("code",200);
map.put("errorMsg","success");
return map;
}
}

2.4 运行结果

使用IDEA自带的Http Client来测试api

结果:

可以看到通过利用AOP,我们没有修改Controller中的代码,就可以实现对Controller中每个方法请求信息的日志记录功能。

而且我们还能够指定该切面类是在生产环境还是开发环境下生效,只需要在切面类上添加注解。

@Profile({"dev"})

然后在配置文件中定义 spring.profiles.active 的属性即可。

3. 总结

因为学习了Spring后,虽然知道有AOP这个东西,但是却从来没有真正的在实际项目中运用,这几天研究日志记录,却发现AOP在日志记录中的妙用,甚至可以利用AOP在对代码无侵入的情况下,进行参数数据的加密和解密操作。但是,虽然说AOP使用方便,但是不能够滥用,毕竟AOP底层使用动态代理,而动态代理要做到对方法的修改就肯定要使用到反射,反射会对性能有影响。

4. 参考文章

(7 封私信 / 66 条消息) 在一个完整的项目中,会用AOP技术么,能用简单易懂的方式说明下什么是AOP么? - 知乎 (zhihu.com)

【SpringBoot】AOP应用实例_sysu_lluozh-CSDN博客

(20条消息) Springboot Aop 自定义注解、切面_张同学的博客-CSDN博客_springboot 自定义注解切面

运用Spring Aop,一个注解实现日志记录的更多相关文章

  1. spring aop通过注解实现日志记录

    首先是几个概念:连接点(Joinpoint).切点(Pointcut).增强(Advice).切面(Aspect) 另外也要使用到注解. 需求:通过注解定义LogEnable.然后程序运行能够识别定义 ...

  2. 利用Spring AOP自定义注解解决日志和签名校验

    转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...

  3. spring AOP自定义注解 实现日志管理

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...

  4. (转)利用Spring AOP自定义注解解决日志和签名校验

    一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: ...

  5. spring AOP自定义注解方式实现日志管理

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在appli ...

  6. Spring Boot 2.X(八):Spring AOP 实现简单的日志切面

    AOP 1.什么是 AOP ? AOP 的全称为 Aspect Oriented Programming,译为面向切面编程,是通过预编译方式和运行期动态代理实现核心业务逻辑之外的横切行为的统一维护的一 ...

  7. spring aop 使用注解方式总结

    spring aop的注解方式:和xml的配置方式略有区别,详细如下: 1.首先还是建立需要的切面类:切面类里面定义好切点配置,以及所有的需要实现的通知方法. /** * */ package com ...

  8. Spring aop+自定义注解统一记录用户行为日志

    写在前面 本文不涉及过多的Spring aop基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...

  9. Spring AOP使用注解记录用户操作日志

    最后一个方法:核心的日志记录方法 package com.migu.cm.aspect; import com.alibaba.fastjson.JSON; import com.migu.cm.do ...

随机推荐

  1. 【九度OJ】题目1065:输出梯形 解题报告

    [九度OJ]题目1065:输出梯形 解题报告 标签(空格分隔): 九度OJ [LeetCode] http://ac.jobdu.com/problem.php?pid=1065 题目描述: 每组测试 ...

  2. 【LeetCode】933. Number of Recent Calls 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 二分查找 队列 相似题目 参考资料 日期 题目地址: ...

  3. 【LeetCode】506. Relative Ranks 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 排序 argsort 堆 日期 题目地址:https ...

  4. 【LeetCode】60. Permutation Sequence 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  5. codeforce A. 2Char(水题,暴力)

    今晚发了个蛇精病,然后CF了,第一题这好难啊,然而水题一个,暴力飘过. 链接http://codeforces.com/contest/593/problem/A: 题意比较难懂吗?傻逼百度都翻译不对 ...

  6. MySQL 中的共享表空间与独立表空间

    对于 InnoDB 存储引擎来说,它可以将每张表存放于独立的表空间,即tablename.ibd 文件:也可以将数据存放于 ibdata 的共享表空间,一般命名是 ibdataX,后面的 X 是一个具 ...

  7. 如何修改文件的 “创建时间” 和 “修改时间”(Windows Linux macOS)

    请访问原文链接:https://sysin.org/blog/how-to-change-file-date,查看最新版.原创作品,转载请保留出处. 作者:gc(at)sysin.org,主页:www ...

  8. Star Way To Heaven

    题目描述 小 x伤心的走上了 Star way to heaven. 到天堂的道路是一个笛卡尔坐标系上一个 n*m的长方形通道 顶点在0,0 和 . 小 n,m 从最左边任意一点进入,从右边任意一点走 ...

  9. 新装CentOS7用yum安装软件提示 cannot find a valid baseurl for repobase7x86_64

    1.打开网络配置文件:vi /etc/sysconfig/network-scripts/ifcfg-ens33(每个机子都可能不一样,但格式会是"ifcfg-e..."). 2. ...

  10. Flask_请求钩子(七)

    在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如: 在请求开始时,建立数据库连接: 在请求开始时,根据需求进行权限校验: 在请求结束时,指定数据的交互格式: 为了让每个视图函数避免编 ...