SpringBoot AOP概念及使用Demo
AOP核心概念
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)-》(通知+切点)
类是对物体特征的抽象,切面就是对横切关注点的抽象。
通知+切点
意思就是所有要被应用到增强(advice)代码的地方。(包括方法的方位信息)
3、连接点(joinpoint)-》(被拦截的方法)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut)-》(描述拦截那些方法的部分)
对连接点进行拦截的定义
5、通知(advice)-》(拦截后执行自己业务逻辑的那些部分)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
这玩意也叫 增强
在逻辑层次上包括了我们抽取的公共逻辑和方位信息。因为Spring只能方法级别的应用AOP,也就是我们常见的before,after,after-returning,after-throwing,around五种,意思就是在方法调用前后,异常时候执行我这段公共逻辑呗。
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程。
比如根据Advice中的方位信息在指定切点的方法前后,执行增强。这个过程Spring 替我们做好了。利用的是CGLIB动态代理技术。
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
图解
上面那一堆看不懂对吗? 我也不太懂。
来看张图
好了,现在我们直接看代码,简单的使用如下:
引入pom
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version><!--$NO-MVN-MAN-VER$--> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version><!--$NO-MVN-MAN-VER$--> </dependency> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.2</version><!--$NO-MVN-MAN-VER$--> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.2</version><!--$NO-MVN-MAN-VER$--> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency> </dependencies>
创建DTO对象
package com.szl.demo.common.dto; import lombok.Data; @Data public class AccountDto { private String accountNo; private String accountName; private Double balance; }
创建service和service实现类
package com.szl.demo.service; import com.szl.demo.common.dto.AccountDto; public interface DemoService { public AccountDto getUserAccount(); }
package com.szl.demo.service.impl; import org.springframework.stereotype.Service; import com.szl.demo.common.dto.AccountDto; import com.szl.demo.service.DemoService; @Service("demoService") public class DemoServiceImpl implements DemoService { public AccountDto getUserAccount() { AccountDto dto = new AccountDto(); dto.setAccountNo("22133232001"); dto.setAccountName("XXX"); dto.setBalance(3000D); return dto; } }
创建controller
package com.szl.demo.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.szl.demo.common.dto.AccountDto; import com.szl.demo.service.DemoService; @Controller public class DemoController { @Autowired private DemoService demoService;
/**
* @param request
* @param response
* @param body 这个是请求body体
* @param nickName 这个是post参数
* @param birthday 这个是post参数
* @return
*/ @ResponseBody @RequestMapping(value = "/testAopDemo", method = RequestMethod.POST,
consumes = "application/json",
produces = "application/json;charset=UTF-8") public AccountDto testAopDemo(HttpServletRequest request, HttpServletResponse response,
@RequestBody String body,
@RequestParam("nickName") String nickName,
@RequestParam("birthday") String birthday) {
log.info("controller层 请求body内容: " + body);
log.info("controller层 请求参数nickName内容: " + nickName);
log.info("controller层 请求参数birthday内容: " + birthday);
return demoService.getUserAccount();
} }
以上工作全部做完后,现在我们创建AOP核心代码块
package com.szl.demo.common.aop; import java.util.Arrays; import javax.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import lombok.extern.slf4j.Slf4j; /** * @author Jimmy Shan * @desc 创建AOP 切面记录controller的日志记录 * @Slf4j 必须结合lombok一起使用,否则无效 */ @Slf4j @Aspect @Component public class WebLogAcpect {
// 用于记录每个controller执行的耗时时间,毫秒级
private ThreadLocal<Long> timeLocal = new ThreadLocal<>(); /** * 定义切入点,切入点为com.example.aop下的所有函数 */ @Pointcut("execution(public * com.szl.demo.controller.*.*(..))") public void webLog(){} /** * @param joinPoint * @throws Throwable * @desc 前置通知:在连接点之前执行的通知 */ @Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
timeLocal.set(startTime);// 记录开始时间
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
log.info("URL : " + request.getRequestURL().toString());
log.info("HTTP_METHOD : " + request.getMethod());
log.info("IP : " + request.getRemoteAddr());
log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
log.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
//获取请求参数信息 和 requestBody数据
List<String> paramList = new ArrayList<>();
Object[] objArr = joinPoint.getArgs();
for (int i = 0; i < objArr.length; i++) {
if (objArr[i] instanceof String) {
paramList.add(String.valueOf(objArr[i]));
}
}
log.info("AOP获取参数内容为: " + String.join("###", paramList));
} /** * @param ret * @throws Throwable * @desc 处理完成请求,返回的信息 */ @AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
log.info("返回的内容: " + ret);
log.info("耗时: {} 毫秒", (System.currentTimeMillis() - timeLocal.get().longValue()));
timeLocal.remove();
} }
OK,到此我们的AOP就可以正常的工作了,我们来看一下效果,控制台日志信息输出如下:
00:33:37.167 [http-nio-8081-exec-1] INFO - Initializing Spring DispatcherServlet 'dispatcherServlet' 00:33:37.167 [http-nio-8081-exec-1] INFO - Initializing Servlet 'dispatcherServlet' 00:33:37.171 [http-nio-8081-exec-1] INFO - Completed initialization in 2 ms 00:33:37.218 [http-nio-8081-exec-1] INFO - URL : http://127.0.0.1:8081/testAopDemo 00:33:37.218 [http-nio-8081-exec-1] INFO - HTTP_METHOD : POST 00:33:37.218 [http-nio-8081-exec-1] INFO - IP : 127.0.0.1 00:33:37.218 [http-nio-8081-exec-1] INFO - CLASS_METHOD : com.szl.demo.controller.DemoController.testAopDemo 00:33:37.218 [http-nio-8081-exec-1] INFO - ARGS : [org.apache.catalina.connector.RequestFacade@69bd93bb, org.apache.catalina.connector.ResponseFacade@5a33b12, {"info2":"haha", "name":"Jackson"}, 百里守约, 2019-06-05] 00:33:37.218 [http-nio-8081-exec-1] INFO - AOP获取参数内容为: {"info2":"haha", "name":"Jackson"}###百里守约###2019-06-05 00:33:37.234 [http-nio-8081-exec-1] INFO - controller层 请求body内容: {"info2":"haha", "name":"Jackson"} 00:33:37.234 [http-nio-8081-exec-1] INFO - controller层 请求参数nickName内容: 百里守约 00:33:37.234 [http-nio-8081-exec-1] INFO - controller层 请求参数birthday内容: 2019-06-05 00:33:37.234 [http-nio-8081-exec-1] INFO - 返回的内容: AccountDto(accountNo=22133232001, accountName=XXX, balance=3000.0) 00:33:37.234 [http-nio-8081-exec-1] INFO - 耗时: 16 毫秒
蓝色字体部分就是 AOP 和 controller 获取参数的值信息。
转自:https://www.cnblogs.com/lic309/p/4079194.html
https://blog.csdn.net/lmb55/article/details/82470388
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)