AOP 是 Spring 体系中非常重要的两个概念之一(另外一个是 IoC),今天这篇文章就来带大家通过实战的方式,在编程猫 SpringBoot 项目中使用 AOP 技术为 controller 层添加一个切面来实现接口访问的统一日志记录。

一、关于 AOP

AOP,也就是 Aspect-oriented Programming,译为面向切面编程,是计算机科学中的一个设计思想,旨在通过切面技术为业务主体增加额外的通知(Advice),从而对声明为“切点”(Pointcut)的代码块进行统一管理和装饰。

这种思想非常适用于,将那些与核心业务不那么密切关联的功能添加到程序中,就好比我们今天的主题——日志功能,就是一个典型的案例。

AOP 是对面向对象编程(Object-oriented Programming,俗称 OOP)的一种补充,OOP 的核心单元是类(class),而 AOP 的核心单元是切面(Aspect)。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而降低耦合度,提高程序的可重用性,同时也提高了开发效率。

我们可以简单的把 AOP 理解为贯穿于方法之中,在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。

二、AOP 的相关术语

来看下面这幅图,这是一个 AOP 的模型图,就是在某些方法执行前后执行一些通用的操作,并且这些操作不会影响程序本身的运行。



我们了解下 AOP 涉及到的 5 个关键术语:

1)横切关注点,从每个方法中抽取出来的同一类非核心业务

2)切面(Aspect),对横切关注点进行封装的类,每个关注点体现为一个通知方法;通常使用 @Aspect 注解来定义切面。

3)通知(Advice),切面必须要完成的各个具体工作,比如我们的日志切面需要记录接口调用前后的时长,就需要在调用接口前后记录时间,再取差值。通知的方式有五种:

  • @Before:通知方法会在目标方法调用之前执行
  • @After:通知方法会在目标方法调用后执行
  • @AfterReturning:通知方法会在目标方法返回后执行
  • @AfterThrowing:通知方法会在目标方法抛出异常后执行
  • @Around:把整个目标方法包裹起来,在被调用前和调用之后分别执行通知方法

4)连接点(JoinPoint),通知应用的时机,比如接口方法被调用时就是日志切面的连接点。

5)切点(Pointcut),通知功能被应用的范围,比如本篇日志切面的应用范围是所有 controller 的接口。通常使用 @Pointcut 注解来定义切点表达式。

切入点表达式的语法格式规范如下所示:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?)
  • modifiers-pattern? 为访问权限修饰符
  • ret-type-pattern 为返回类型,通常用 * 来表示任意返回类型
  • declaring-type-pattern? 为包名
  • name-pattern 为方法名,可以使用 * 来表示所有,或者 set* 来表示所有以 set 开头的类名
  • param-pattern) 为参数类型,多个参数可以用 , 隔开,各个参与也可以使用 * 来表示所有类型的参数,还可以使用 (..) 表示零个或者任意参数
  • throws-pattern? 为异常类型
  • ? 表示前面的为可选项

举个例子:

@Pointcut("execution(public * com.codingmore.controller.*.*(..))")

表示 com.codingmore.controller 包下的所有 public 方法都要应用切面的通知。

三、实操 AOP 记录接口访问日志

第一步,在 Spring Boot 项目的 pom.xml 文件中添加 spring-boot-starter-aop 依赖。

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

第二步,添加日志信息封装类 WebLog,用于记录什么样的操作、操作的人是谁、开始时间、花费的时间、操作的路径、操作的方法名、操作主机的 IP、请求参数、返回结果等。

/**
* Controller层的日志封装类
* Created by macro on 2018/4/26.
*/
public class WebLog {
private String description;
private String username;
private Long startTime;
private Integer spendTime;
private String basePath;
private String uri;
private String url;
private String method;
private String ip;
private Object parameter;
private Object result;
//省略了getter,setter方法
}

第三步,添加统一日志处理切面 WebLogAspect。

/**
* 统一日志处理切面
* Created by 石磊
*/
@Aspect
@Component
@Order(1)
public class WebLogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class); @Pointcut("execution(public * com.codingmore.controller.*.*(..))")
public void webLog() {
} @Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
} @AfterReturning(value = "webLog()", returning = "ret")
public void doAfterReturning(Object ret) throws Throwable {
} @Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
//获取当前请求对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//记录请求信息(通过Logstash传入Elasticsearch)
WebLog webLog = new WebLog();
Object result = joinPoint.proceed();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation log = method.getAnnotation(ApiOperation.class);
webLog.setDescription(log.value());
}
long endTime = System.currentTimeMillis();
String urlStr = request.getRequestURL().toString();
webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
webLog.setIp(request.getRemoteUser());
Map<String,Object> logMap = new HashMap<>();
logMap.put("spendTime",webLog.getSpendTime());
logMap.put("description",webLog.getDescription());
LOGGER.info("{}", JSONUtil.parse(webLog));
return result;
}
}

第四步,运行项目,并对 controller 下的某个控制器进行测试。

Swagger knife4j 访问地址:http://localhost:9022/doc.html

执行登录用户查询操作:



可以在控制台可以看到以下日志信息:



源码地址:

https://github.com/itwanger/coding-more

参考链接:

作者 cxuan:https://www.cnblogs.com/cxuanBlog/p/13060510.html

灰小猿:https://bbs.huaweicloud.com/blogs/289045

山高我为峰:https://www.cnblogs.com/liaojie970/p/7883687.html

macrozheng:https://github.com/macrozheng/mall


本篇已收录至 GitHub 上星标 1.6k+ star 的开源专栏《Java 程序员进阶之路》,据说每一个优秀的 Java 程序员都喜欢她,风趣幽默、通俗易懂。内容包括 Java 基础、Java 并发编程、Java 虚拟机、Java 企业级开发、Java 面试等核心知识点。学 Java,就认准 Java 程序员进阶之路

https://github.com/itwanger/toBeBetterJavaer

star 了这个仓库就等于你拥有了成为了一名优秀 Java 工程师的潜力。也可以戳下面的链接跳转到《Java 程序员进阶之路》的官网网址,开始愉快的学习之旅吧。

https://tobebetterjavaer.com/

没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟

Spring Boot AOP 扫盲,实现接口访问的统一日志记录的更多相关文章

  1. Spring Boot MyBatis 数据库集群访问实现

    Spring Boot MyBatis 数据库集群访问实现 本示例主要介绍了Spring Boot程序方式实现数据库集群访问,读库轮询方式实现负载均衡.阅读本示例前,建议你有AOP编程基础.mybat ...

  2. Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理

    Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理   本文链接:https://blog.csdn.net/puhaiyang/article/details/78146620 ...

  3. Spring Boot实现通用的接口参数校验

    Spring Boot实现通用的接口参数校验 Harries Blog™ 2018-05-10 2418 阅读 http ACE Spring App API https AOP apache IDE ...

  4. Spring Boot Hello World (restful接口)例子

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  5. Spring Boot AOP解析

    Spring Boot AOP 面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面. AOP(Aspec ...

  6. spring boot / cloud (二) 规范响应格式以及统一异常处理

    spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...

  7. Spring Boot干货系列:(七)默认日志框架配置

    Spring Boot干货系列:(七)默认日志框架配置 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候, ...

  8. (转)Spring Boot干货系列:(七)默认日志logback配置解析

    转:http://tengj.top/2017/04/05/springboot7/ 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候,是带着下面几个问题来查资料的, ...

  9. Spring Boot从入门到精通(八)日志管理实现和配置信息分析

    Spring Boot对日志的处理,与平时我们处理日志的方式完全一致,它为Java Util Logging.Log4J2和Logback提供了默认配置.对于每种日志都预先配置使用控制台输出和可选的文 ...

随机推荐

  1. vue中使用window.resize并去抖动优化

    this.clientWidth = document.documentElement.clientWidth window.onresize = () => { this.clientWidt ...

  2. 华为HMS Core全新推出会员转化&留存预测模型

    现在,付费学知识,付费听歌,付费看电视剧,付费享受线上购物优惠--等等场景已经成为大部分年轻人的日常. 而对于企业商家来说,付费会员作为企业差异化用户运营的手段,不仅有利于提升用户的品牌忠诚度,在当下 ...

  3. Kubernetes最佳实践之腾讯云TKE 集群组建

    作者陈鹏,腾讯工程师,负责腾讯云 TKE 的售中.售后的技术支持,根据客户需求输出合理技术方案与最佳实践,为客户业务保驾护航.使用 TKE 来组建 Kubernetes 集群时,会面对各种配置选项,本 ...

  4. day 8 求平均数

    fun()函数的功能:计算形参x所指数组中N个数的平均值,(这里全部取浮点数) 并输出,在将大于平均值的数放在形参y所指的数组中,在主函数输出. 效果还不错: 还存在需要优化的分析:其实存到y数组中的 ...

  5. [源码分析] Facebook如何训练超大模型---(1)

    [源码分析] Facebook如何训练超大模型---(1) 目录 [源码分析] Facebook如何训练超大模型---(1) 0x00 摘要 0x01 简介 1.1 FAIR & FSDP 1 ...

  6. [硬拆解]拆解一个USB转CAN总线设备-PCAN-USB

    介绍 PCAN-USB适配器可以简单地连接到CAN网络.其紧凑的塑料外壳使它适合移动应用.光电去耦版隔离了PC和CAN端之间高达500伏特的电流隔离. 该包还提供了Windows的CAN monito ...

  7. 从SQL Server数据库导出SQL语句

    不同于直接 备份/恢复 或者 导入/导出 数据库操作. 新版本SQL Server客户端中还可以生成相对应的SQL语句. 非常方便与查看和与其他人共享. 连接上数据库后, 右击数据库, 选择 Gene ...

  8. 浅析DOM 与 html ,xml。

    DOM= Document Object Model,文档对象模型,DOM可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构.是表示和处理一个HTML或XML文档的常用方法. DOM定义了 ...

  9. vue学习11-监听属性

    <!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta http ...

  10. [JavaWeb]利用JSP的编码特性制作免杀后门

    利用JSP的编码特性制作免杀后门 这里是借鉴了Y4stacker师傅的thinkings 待解决的问题 JSP解析 JSP"乱码"为什么还能被识别 "乱码"的J ...