介绍
这个项目是一个基于 Spring 框架 的面向切面编程(AOP)案例,展示了如何通过 AOP 在方法执行的不同阶段(前置、后置、返回值)插入日志记录功能。项目通过实现一个简单的服务类 UserinfoService,并利用切面类 LoggingAspect 来分别在方法调用前后记录日志信息。
classDiagram
MainApp --> AppConfig : Uses
MainApp --> UserinfoService : Calls
UserinfoService --> LoggingAspect : AOP advice
AppConfig --> LoggingAspect : Configures
AppConfig --> UserinfoService : Configures
解释
MainApp 依赖于 AppConfig 配置类,获取 UserinfoService Bean 并调用方法。
UserinfoService 受到 LoggingAspect 的 AOP 通知(切面)。
AppConfig 配置类配置了 UserinfoService 和 LoggingAspect。
操作
1. 创建项目结构
创建 com.mtw.test 包,并在该包中添加Service.UserinfoService 服务类和 Aspect.LoggingAspect切面类。
同样我们需要一个配置类来告诉 Spring 我们的应用有哪些包需要被扫描、哪些功能需要启用,所以创建一个Config.AppConfig配置类,以及一个Test.MainApp测试类来运行 UserinfoService 中的 foo1 和 foo2 方法,并观察 LoggingAspect 的切面通知是否会执行。
| 文件和文件夹结构示例 | |
|---|---|
| src └── main └── java └── com └── mtw └── test ├── aspect │ └── LoggingAspect.java ├── service │ └── UserinfoService.java ├── config │ └── AppConfig.java └── Test └── MainApp.java | ![]() |
2. 定义 UserinfoService 类
这是一个业务逻辑类,负责执行核心功能。在这里,我们定义了两个简单的业务方法:
foo1():打印"foo1被调用"foo2():打印"foo2被调用"
classDiagram
class UserinfoService {
+void foo1()
+void foo2()
}
package com.mtw.test.Service;
import org.springframework.stereotype.Service;
@Service
public class UserinfoService {
public void foo1(){
System.out.println("foo1被调用");
}
public void foo2(){
System.out.println("foo2被调用");
}
}
3. 定义切面类 LoggingAspect
这是一个切面类,用于实现 AOP(面向切面编程)。切面中定义了多个通知方法,这些方法会在 foo1() 和 foo2() 执行时被调用,记录不同阶段的日志。
- 前置通知:方法执行之前触发,记录 "前置通知"。
- 后置通知:方法执行之后触发,记录 "后置通知"。
- 返回值通知:方法执行完并返回值之后触发,记录 "返回值通知"。
[!WARNING]
要使用
@Aspect注解和 Spring AOP 功能,确保在项目的pom.xml文件中添加以下 Spring AOP 依赖:<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.10</version> <!-- 使用适合你的Spring版本 -->
</dependency>此外,还需要以下依赖,以确保 Spring AOP 能正常工作:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.10</version>
</dependency>这些依赖项允许项目使用
@Aspect注解并支持面向切面的编程功能。
classDiagram
class LoggingAspect {
+void beforeFoo1()
+void afterFoo1()
+void afterReturningFoo1()
+void beforeFoo2()
+void afterFoo2()
+void afterReturningFoo2()
}
package com.mtw.test.Aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.mtw.test.Service.*.*(..))")
public void beforeAdvice() {
System.out.println("前置通知");
}
@After("execution(* com.mtw.test.Service.*.*(..))")
public void afterAdvice() {
System.out.println("后置通知");
}
@AfterReturning("execution(* com.mtw.test.Service.*.*(..))")
public void afterReturningAdvice() {
System.out.println("返回值通知");
}
}
4. 配置 Spring AOP
这个类是 Spring 配置类,配置了 Spring AOP 和组件扫描。通过 @EnableAspectJAutoProxy 启用 AOP 功能,并通过 @ComponentScan 告诉 Spring 扫描 com.mtw.test 包下的所有组件(如 UserinfoService 和 LoggingAspect)。
classDiagram
class AppConfig {
+@Configuration
+@ComponentScan(basePackages="com.mtw.test")
+@EnableAspectJAutoProxy
}
package com.mtw.test.Config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "com.mtw.test")
@EnableAspectJAutoProxy
public class Appconfig {
}
5. 测试代码
这是一个简单的测试类,负责启动 Spring 容器并调用 UserinfoService 中的方法(foo1() 和 foo2())。通过运行这个类,你可以验证切面通知是否在方法执行的各个阶段被正确触发。
classDiagram
class MainApp {
+void main(String[] args)
+ApplicationContext context
+UserinfoService userinfoService
}
package com.mtw.test.Test;
import com.mtw.test.Config.Appconfig;
import com.mtw.test.Service.UserinfoService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Appconfig.class);
UserinfoService userinfoService = context.getBean(UserinfoService.class);
userinfoService.foo1();
System.out.println("----------");
userinfoService.foo2();
}
}
6. 运行和输出
运行 MainApp,控制台输出应如下:
| 运行结果 | |
|---|---|
| 前置通知 foo1被调用 返回值通知 后置通知 ---------- 前置通知 foo2被调用 返回值通知 后置通知 | ![]() |
7. 解释
sequenceDiagram
participant C as Client
participant S as UserinfoService
participant L as LoggingAspect
C->>S: Call foo1()
L->>S: 前置通知 (Before Advice)
S->>S: 执行 foo1 方法
L->>S: 返回值通知 (After Returning Advice)
L->>S: 后置通知 (After Advice)
C->>S: Call foo2()
L->>S: 前置通知 (Before Advice)
S->>S: 执行 foo2 方法
L->>S: 返回值通知 (After Returning Advice)
L->>S: 后置通知 (After Advice)
- 前置通知:在目标方法执行前触发
- 后置通知:在目标方法执行后触发,无论是否抛出异常
- 返回值通知:在目标方法正常执行并返回后触发


