1.Spring AOP (Aspect-Oriented Programming)
1. 1. 什么是 Spring AOP?
- AOP(面向切面编程) 是 Spring 提供的一种可插拔的组件技术,允许我们在软件运行过程中添加额外的功能。
- 场景:假设有两个模块,用户管理模块A和员工管理模块B。现在需要在业务处理过程中添加权限过滤功能。
- 如果在两个模块中都添加权限判断代码,当权限需求变化时,需要再次修改代码,这样会增加开发和维护成本。
- 使用 Spring AOP,我们可以将权限判断的代码独立为一个切面,在代码执行前进行权限过滤,而不需要修改原业务逻辑。
1. 2. 面向切面编程 (AOP) 的核心概念
- 切面(Aspect):表示横切的功能模块,用于实现某些通用功能(如权限检查、日志记录等)。切面可以在方法执行前后插入。
- 权限切面:在执行业务逻辑之前判断用户权限。
- 日志切面:记录业务逻辑的执行时间、输入参数、输出结果等信息。
- 通过切面技术,日志和权限判断代码可以在不修改业务代码的情况下被“织入”程序。
- 如果业务需求发生变化,只需调整配置即可轻松移除切面,不影响核心业务逻辑。
1. 3. 切面与插件技术的类比
- 切面类似于我们在浏览器中安装的插件,可以为现有的业务模块增加额外的功能。
- 例如:安装翻译插件后,浏览器可以将英文网页自动翻译为中文。
- 一旦不需要这些功能,卸载插件即可,还原浏览器的原始状态。
- 切面也是如此,它为业务模块提供了额外的功能,但这些模块本身不会感知到切面的存在。
1. 4. 为什么叫“切面”?
- 正常的软件执行流程是从上到下按照代码顺序执行的,而切面则像一个横切面,在执行过程中横插进入业务流程中。
- 这些横切的功能模块就是所谓的“切面(Aspect)”,通过切面我们可以为现有的业务逻辑增加扩展功能。
1. 5. AOP 的最终目的
- 不修改源码 的情况下扩展程序行为。
- 通常将与业务无关的通用功能(如权限检查、日志记录)封装为切面类,通过配置来插入这些功能。
- 切面可以配置在目标方法的执行前、执行后,达到真正的“即插即用”。
2.Spring AOP - 实战配置项目
课程简介
本节课程将通过实际项目配置,带领大家一步一步理解 Spring AOP(面向切面编程)的功能。我们将基于 XML 配置的形式来实现 AOP,并通过演示了解 AOP 如何对现有系统进行功能扩展,而无需修改源代码。
2. 1. 项目结构介绍
- 本次演示基于 s01 工程,其中包含了两个主要部分:
- DAO 层:包括
EmployeeDao
和UserDao
,分别用于对员工表和用户表的数据增删改查。 - Service 层:包括
EmployeeService
和UserService
,分别提供了员工相关的业务逻辑和用户管理的业务逻辑。EmployeeService
:提供entry
方法,模拟员工入职操作。UserService
:提供createUser
方法(创建用户)和generateRandomPassword
方法(生成随机密码)。
- DAO 层:包括
这些类的业务逻辑非常常规,但本节课我们将通过 AOP 实现对方法执行时间的监控,解决手动添加代码带来的冗余和复杂度问题。
2. 2. 需求描述
我们希望在系统运行过程中,对所有 Service
层和 DAO
层的方法调用前打印执行时间,从而便于分析系统负载高峰时间。
问题:
- 如果直接在每个方法中手动添加
System.out.println()
代码,维护和删除这些代码将变得非常麻烦。 - AOP 可以在不修改原代码的情况下,灵活地添加或移除这些功能。
2. 3. 配置项目依赖
首先,我们需要在 pom.xml
文件中添加必要的依赖项:
<dependencies>
<!-- Spring context dependency -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- AspectJ Weaver (AOP 底层依赖) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
spring-context
是用来初始化 IOC 容器的基础依赖,而 aspectjweaver
则是 AOP 的底层依赖,负责切面功能的实现。
2. 4. 配置 applicationContext.xml
接下来,我们需要在 resources
目录下创建 applicationContext.xml
文件,这是 Spring IOC 的配置文件。
添加命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置 Beans -->
<bean id="userDao" class="com.example.dao.UserDao" />
<bean id="employeeDao" class="com.example.dao.EmployeeDao" />
<bean id="userService" class="com.example.service.UserService">
<property name="userDao" ref="userDao" />
</bean>
<bean id="employeeService" class="com.example.service.EmployeeService">
<property name="employeeDao" ref="employeeDao" />
</bean>
</beans>
引入 aop
命名空间:
该命名空间用于配置 AOP 所需的相关标签。它将帮助我们在不修改源代码的前提下,为现有方法添加执行时间打印功能。
2. 5. 初始化 IOC 容器并执行测试
接下来,我们在 aop
包下创建一个 Spring 应用的入口类:
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.createUser(); // 模拟创建用户的过程
}
}
运行代码后,可以看到控制台输出显示了 UserService
和 UserDao
中各个方法的执行情况。
2. 6. Spring AOP - 方法执行时间打印需求实现
课程目标
通过 AOP 实现对 Service
或 DAO
层中任意方法的执行时间进行打印,并避免在每个方法中手动增加日志打印代码。AOP 能够灵活地实现这些功能,且无需修改源代码。
1. 新增切面类 (Method Aspect)
在 AOP 配置中,我们需要创建一个切面类,用于扩展业务逻辑。在 aop
包下新增一个 aspect
包,创建切面类 MethodAspect
,用于打印方法的执行时间。
切面类 MethodAspect
public class MethodAspect {
public void printExecutionTime(JoinPoint joinPoint) {
// 获取当前时间并格式化
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String now = sdf.format(new Date