- IoC (控制反转 / Inversion of Control)
概念
- 核心思想: 将对象创建、依赖关系管理、生命周期控制的责任从应用程序代码中"反转"给一个外部容器(即 Spring IoC 容器)来管理。
- 传统方式: 在传统的开发模式中,对象 A 如果需要使用对象 B,通常会在 A 的内部直接
new B()来创建 B 的实例。对象 A 主动创建并控制对象 B 的生命周期。 - IoC 方式: 对象 A 不再负责创建其依赖的对象 B。而是由 Spring 容器负责创建对象 B(以及 A 所需的其他依赖对象),并在创建对象 A 时,将已经创建好的对象 B "注入"给对象 A。对象 A 是被动地接受其所依赖的对象。
实现机制:DI (依赖注入 / Dependency Injection)
- DI 是 IoC 的一种具体实现方式。Spring 主要通过 DI 来实现 IoC。
- 依赖注入的方式:
- 构造器注入: 通过类的构造函数来注入依赖。
- Setter 注入: 通过类的 Setter 方法(或符合 JavaBean 规范的属性设置方法)来注入依赖。
- 字段注入: 直接在字段上标注注解(如
@Autowired)进行注入(虽然方便,但不推荐作为首选,因为可能破坏封装性和可测试性)。 - 接口注入: 较少使用。
关键组件:Spring IoC 容器
- 负责管理应用中的对象(在 Spring 中称为 Bean)。
- 核心接口是
BeanFactory及其更高级的子接口ApplicationContext。 - 容器读取配置(XML 配置文件或基于注解的配置),根据配置信息创建 Bean 实例,管理它们之间的依赖关系,并在需要时提供这些 Bean。
应用场景
- 解耦: 最核心的价值。对象之间的依赖关系由容器管理,降低了代码的耦合度。修改一个类的依赖或实现时,对其他类的影响降到最低(通常只需修改配置)。
- 可测试性: 依赖可以被轻松地替换(例如,用 Mock 对象替换真实依赖进行单元测试)。
- 配置灵活性: 通过配置文件或注解,可以方便地切换实现类(例如,切换不同的数据源实现、切换不同的缓存实现)。
- 管理单例: Spring 容器默认管理的是单例 Bean,有效控制对象的创建数量。
- 生命周期管理: 容器可以管理 Bean 的初始化(
init-method)和销毁(destroy-method)过程。
示例 (使用注解)
@Service // 告诉 Spring 这是一个需要管理的 Bean
public class UserServiceImpl implements UserService {
@Autowired // Spring 容器会自动查找并注入一个 UserRepository 类型的 Bean
private UserRepository userRepository;
@Override
public User getUserById(Long id) {
return userRepository.findById(id);
}
}
@Repository // 告诉 Spring 这是一个需要管理的 Bean (通常标注在数据访问层)
public class JdbcUserRepository implements UserRepository {
@Override
public User findById(Long id) {
// JDBC 查询数据库的实现
return ...;
}
}
2. AOP (面向切面编程 / Aspect-Oriented Programming)
概念
- 核心思想: 将那些散布在多个类或方法中的横切关注点(Cross-cutting Concerns)从核心业务逻辑中分离出来,集中到一个地方进行声明和管理。
- 横切关注点: 指那些与核心业务功能无关,但又需要在多个地方重复使用的功能模块。例如:
- 日志记录
- 安全控制(权限检查)
- 事务管理
- 性能监控
- 异常处理
- 传统 OOP 的局限: 这些横切关注点会分散在各个业务方法中,导致代码重复(多个方法里写相同的日志代码)、代码混乱(业务逻辑与非业务逻辑混杂)、难以维护。
AOP 关键术语
- 切面: 封装横切关注点的模块。它定义了 Advice 和 Pointcut。
- 连接点: 程序执行过程中的一个点,例如方法的调用、方法的执行、字段的访问、异常抛出等。在 Spring AOP 中,主要关注的是方法执行。
- 切入点: 一个表达式,用于匹配哪些连接点 需要应用特定的 Advice。它定义了 "在何处执行增强"。
- 通知: 定义了 "在何时执行" 以及 "执行什么"。它是切面在特定连接点执行的动作。类型有:
- 前置通知 (
@Before):在目标方法执行前执行。 - 后置通知 (
@After):在目标方法执行后执行(无论是否抛出异常)。 - 返回通知 (
@AfterReturning):在目标方法成功返回后执行。 - 异常通知 (
@AfterThrowing):在目标方法抛出异常后执行。 - 环绕通知 (
@Around):包围目标方法的执行,可以在方法执行前后自定义行为,甚至决定是否执行目标方法。
- 前置通知 (
- 织入: 将切面应用到目标对象并创建代理对象的过程。Spring AOP 主要使用运行时织入(通过 JDK 动态代理或 CGLIB 生成代理对象)。
实现机制 (Spring AOP)
- Spring AOP 是一个基于代理的 AOP 框架。
- 当 Spring 容器创建一个被通知的 Bean 时,它实际上创建的是该 Bean 的代理对象。
- 当调用该 Bean 的方法时,调用会先到达代理对象。代理对象根据配置的切入点表达式判断是否需要应用通知。如果需要,则执行相应的通知逻辑,然后再决定是否调用目标对象的实际方法(在环绕通知中体现)。
应用场景
- 声明式事务管理: 这是 Spring AOP 最经典的应用。通过
@Transactional注解,将事务的开始、提交、回滚等操作以 AOP 方式应用到 Service 层方法上,业务代码无需显式处理事务。 - 日志记录: 统一记录方法调用的入参、返回值、执行时间、异常信息等。
- 安全性: 在方法调用前进行权限检查(如
@PreAuthorize)。 - 性能监控: 统计方法的执行时间。
- 错误处理: 统一处理特定类型的异常,进行日志记录或转换。
- 缓存: 在方法调用前后进行缓存数据的存取(如
@Cacheable)。 - 审计: 记录关键操作的操作人和操作时间。
示例 (使用注解定义切面)
@Aspect // 声明这是一个切面
@Component // 让 Spring 管理这个 Bean
public class LoggingAspect {
// 定义切入点:匹配 com.example.service 包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayerPointcut() {}
// 前置通知:在匹配的方法执行前执行
@Before("serviceLayerPointcut()")
public void logBeforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("即将执行方法: " + methodName + ", 参数: " + Arrays.toString(args));
}
// 返回通知:在匹配的方法成功返回后执行
@AfterReturning(pointcut = "serviceLayerPointcut()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 成功执行完毕,返回值: " + result);
}
// 异常通知:在匹配的方法抛出异常后执行
@AfterThrowing(pointcut = "serviceLayerPointcut()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 执行抛出异常: " + ex.getMessage());
}
}
IoC 与 AOP 的关系
- 基础与上层: IoC 容器是 Spring 框架的基础,它负责创建和管理 Bean(包括核心业务对象)。AOP 构建在 IoC 之上,它利用 IoC 容器管理的 Bean 作为目标对象,通过代理机制来增强这些 Bean 的功能。
- 协同工作: 一个典型的 Spring Bean 可能同时受益于 IoC 和 AOP。IoC 负责注入其依赖的其他 Bean,AOP 则负责在其方法执行前后添加横切逻辑(如事务、日志)。AOP 的切面本身也是一个 Bean,由 IoC 容器管理。
总结表
| 特性 | IoC (控制反转) | AOP (面向切面编程) |
|---|---|---|
| 核心目标 | 解耦:管理对象创建和依赖关系 | 分离关注点:集中处理横切逻辑 |
| 主要实现 | DI (依赖注入) | 代理、切入点、通知、切面 |
| 关键组件 | BeanFactory / ApplicationContext、Bean 定义 |
Aspect、Pointcut、Advice、Proxy、Weaving |
| 解决的问题 | 对象间紧耦合、代码重复创建对象、配置管理复杂 | 代码重复(横切逻辑)、逻辑混杂、可维护性差 |
| 典型应用 | 依赖管理、配置切换、单例管理、生命周期回调 | 事务管理、日志记录、安全控制、性能监控、异常处理 |
| 相互关系 | Spring 框架的基础 | 构建在 IoC 之上,增强 IoC 管理的 Bean |
Spring 的 IoC 和 AOP 是框架的两大基石,它们共同作用,极大地提高了代码的模块化程度、可维护性、可测试性和可扩展性,是现代 Java 企业应用开发的核心范式。