Spring AOP 应用
笔记哥 /
03-27 /
36点赞 /
0评论 /
917阅读
# Spring AOP 应用
## 1. 介绍
AOP:面向切面编程,对面向对象编程的一种补充。
AOP可以将一些公用的代码,自然的嵌入到指定方法的指定位置。
比如:

如上图,我们现在有四个方法,我们想在每个方法执行一开始,输出一个日志信息。但是这样做很麻烦,如果有100个、1000个方法,工作量会很大,而且难以维护。这时候就可以通过AOP进行解决。

## 2. 案例实战
### 2.1 需求分析及环境搭建
环境:SpringBoot + SpringBoot Web + SpringBoot AOP。
```csharp
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-aop
org.projectlombok
lombok
```
目标:控制器业务方法,统一进行日志输出。
新建`User`类,包含`id`和`name`属性。
新建`UserController`
```csharp
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/list")
public List list(){
return Arrays.asList(
new User(1,"张三"),
new User(2,"李四"),
new User(3,"王五")
);
}
@GetMapping("/getById/{id}")
public User getById(@PathVariable("id") Integer id){
return new User(id,"张三");
}
@GetMapping("/deleteById/{id}")
public boolean deleteById(@PathVariable("id") Integer id){
return true;
}
}
```
此时,我们的目标就是使用AOP的方式,给这个`list`、`deleteById`和`getById`方法加上日志。
日志要包括调用方法的名称、返回值以及参数列表。
### 2.3 AOP实现
**1. 首先我们要让AOP知道哪些方法需要被AOP处理 -> 通过注解方式进行处理**
```csharp
// 定义一个注解,来标记需要添加日志的方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String value() default "";
}
```
定义好注解后,给需要使用日志的方法添加注解,如:
```csharp
@LogAnnotation("查询用户") // 标记目标方法
@GetMapping("/getById/{id}")
public User getById(@PathVariable("id") Integer id){
return new User(id,"张三");
}
```
**2. 实现切面任务**
新建`LogAspect`类,这就是生成切面对象的类。我们需要用`@Component`注解进行标注,交给IOC容器进行管理。此外,我们要用`@Aspect`注解标注其为一个切面。
然后,我们要将这个切面与我们刚刚标注的`@LogAnnotation`注解建立联系,让切面知道从哪个位置进行切入。实现的方法为,新建一个方法,然后给这个方法添加`@Pointcut("@annotation(自定义注解的全类名)")`。这样就成功建立的联系。
确定切入点后,我们就可以写切面的实际任务了。新建一个方法`around`。此时,我们要将确定切点的方法与切面实际处理任务的方法进行关联。实现的方法为,给实际处理任务的方法添加`@Around("标记切点的方法名()")`注解。
此时,我们只有一个`around`方法,要用这一个方法对`list`、`getById`、`deleteById`三个方法进行处理。那么`around`方法如何分辨这三个不同的方法呢?这时就需要用到一个连接点对象`ProceedingJoinPoint`。`around`的返回值为object类型,其要返回所切入方法的返回值。
然后,就可以实现日志输出功能了。
```csharp
@Aspect
@Component
@Slf4j
public class LogAspect {
@Pointcut("@annotation(cn.codewei.aopstudy.annotation.LogAnnotation)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 方法名称
String name = point.getSignature().getName();
// 通过反射 获取注解中的内容
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
String value = annotation.value();
// 输出日志
log.info("方法名称:{}, 方法描述: {}, 返回值: {}, 参数列表: {}", name, value, point.proceed(), point.getArgs());
// 返回切入方法的返回值
return point.proceed();
}
}
```
>
>
> @Around、@Before、@After区别
>
>
> - @Before前置通知,是在所拦截方法执行之前执行一段逻辑,返回值类型为void。
> - @After 后置通知,是在所拦截方法执行之后执行一段逻辑,返回值类型为void。
> - @Around 环绕通知,是可以同时在所拦截方法的前后执行一段逻辑,用这个注解的方法入参传的是ProceedingJoinPoint,返回结果类型为Object,返回结果为ProceedingJoinPoint对象的.proceed();
>
>
## 3. @Pointcut
- *使用within表达式匹配*
匹配com.leo.controller包下所有的类的方法
```csharp
@Pointcut("within(com.leo.controller..*)")
public void pointcutWithin(){
}
```
- *this匹配目标指定的方法,此处就是HelloController的方法*
```csharp
@Pointcut("this(com.leo.controller.HelloController)")
public void pointcutThis(){
}
```
- *target匹配实现UserInfoService接口的目标对象*
```csharp
@Pointcut("target(com.leo.service.UserInfoService)")
public void pointcutTarge(){
}
```
- *bean匹配所有以Service结尾的bean里面的方法*
注意:使用自动注入的时候默认实现类首字母小写为bean的id
```csharp
@Pointcut("bean(*ServiceImpl)")
public void pointcutBean(){
}
```
- *args匹配第一个入参是String类型的方法*
```csharp
@Pointcut("args(String, ..)")
public void pointcutArgs(){
}
```
- *@annotation匹配是@Controller类型的方法*
```csharp
@Pointcut("@annotation(org.springframework.stereotype.Controller)")
public void pointcutAnnocation(){
}
```
- *@within匹配@Controller注解下的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.CLASS)*
```csharp
@Pointcut("@within(org.springframework.stereotype.Controller)")
public void pointcutWithinAnno(){
}
```
- *@target匹配的是@Controller的类下面的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.RUNTIME)*
```csharp
@Pointcut("@target(org.springframework.stereotype.Controller)")
public void pointcutTargetAnno(){
}
```
- *@args匹配参数中标注为@Sevice的注解的方法*
```csharp
@Pointcut("@args(org.springframework.stereotype.Service)")
public void pointcutArgsAnno(){
}
```
- *使用excution表达式*
```csharp
@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))")
public void pointCut() {
}
```
本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/1766
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利