Spring IoC 和 AOP 的核心原理是什么?

0 阅读7分钟

Spring 最核心的两个能力就是 IoCAOP
IoC 解决的是“对象由谁创建、谁维护”的问题,AOP 解决的是“如何在不修改业务代码的前提下,给方法统一增强功能”的问题。

一、Spring IoC 的核心原理

1. 什么是 IoC

IoC 全称是 Inversion of Control,控制反转

在传统开发中,一个对象如果依赖另一个对象,通常会在类内部自己去创建:

class UserService {
    private UserDao userDao = new UserDao();
}

这样做的问题是,业务类自己掌握了依赖对象的创建权,类与类之间耦合很高。

而在 Spring 中,对象不再自己创建依赖,而是交给 Spring 容器统一管理:

@Service
class UserService {
    @Autowired
    private UserDao userDao;
}

这就是控制反转:
原本由程序自己控制对象的创建和依赖关系,现在反过来交给 Spring 容器控制。


2. IoC 的本质

IoC 的本质就是三件事:

  • 对象的创建交给容器
  • 对象之间的依赖关系交给容器维护
  • 对象的整个生命周期交给容器管理

所以,Spring IoC 容器本质上可以理解成一个“高级对象工厂”。


3. IoC 的底层实现原理

Spring IoC 能实现,核心依赖以下几个东西:

(1)配置元数据

Spring 需要知道哪些类要交给容器管理、对象之间如何依赖,这些信息来自配置元数据,例如:

  • XML 配置
  • 注解:@Component@Service@Repository@Controller
  • Java 配置类:@Configuration@Bean

这些元数据最终都会被 Spring 解析成统一的内部结构:BeanDefinition

BeanDefinition 可以理解为 Bean 的定义信息,里面记录了:

  • Bean 对应的类
  • 作用域
  • 是否懒加载
  • 依赖关系
  • 初始化方法
  • 销毁方法等

(2)反射机制

Spring 创建对象和注入依赖时,不可能把所有类写死,因此底层需要依赖 Java 的 反射机制 来动态完成:

  • 类的加载
  • 对象的实例化
  • 字段赋值
  • 方法调用

例如:Spring 通过反射创建 Bean 实例,再通过反射给属性赋值,完成依赖注入。


(3)容器机制

Spring 提供了 IoC 容器来统一管理 Bean,核心接口有:

  • BeanFactory:最基础的容器
  • ApplicationContext:功能更完整,实际开发中最常用

容器负责保存 BeanDefinition、创建 Bean、注入依赖、执行生命周期回调、维护单例池等。


4. IoC 的工作流程

Spring IoC 的大体流程如下:

第一步:读取配置

Spring 启动后,会读取 XML、注解、配置类等元数据。

第二步:解析成 BeanDefinition

Spring 将这些元数据统一解析成 BeanDefinition,并注册到 BeanDefinitionRegistry 中。

第三步:实例化 Bean

Spring 根据 BeanDefinition,通过反射创建对象实例。

第四步:依赖注入

Spring 将当前 Bean 所依赖的其他 Bean 注入进来。
注入方式常见有:

  • 构造器注入
  • setter 注入
  • 字段注入

这里常说的 DI(Dependency Injection,依赖注入) ,其实就是 IoC 的具体实现方式。
也就是说:

  • IoC 是思想
  • DI 是手段

第五步:初始化

Bean 完成依赖注入之后,会执行一系列初始化逻辑,例如:

  • Aware 接口回调
  • BeanPostProcessor 前置处理
  • @PostConstruct
  • InitializingBean.afterPropertiesSet()
  • 自定义 init-method
  • BeanPostProcessor 后置处理

第六步:放入容器缓存

如果是单例 Bean,会放到单例池中,后续直接复用。

第七步:销毁

容器关闭时,会调用销毁方法,例如:

  • @PreDestroy
  • DisposableBean.destroy()
  • 自定义 destroy-method

二、Spring AOP 的核心原理

1. 什么是 AOP

AOP 全称是 Aspect Oriented Programming,面向切面编程

它的目标是:
把日志、事务、权限、监控、异常处理等横切逻辑从业务代码中抽离出来,统一处理。

比如很多业务方法都要做日志打印、事务控制,如果每个方法里都手写,会造成大量重复代码。AOP 就是为了解决这种问题。


2. AOP 的本质

AOP 的本质就是:

在目标方法执行前、执行后或异常时,动态插入增强逻辑。

Spring AOP 的底层核心实现,就是 动态代理


3. AOP 的核心概念

理解 Spring AOP,通常要掌握以下几个概念:

(1)切面 Aspect

封装横切逻辑的类,例如日志切面、事务切面。

(2)连接点 JoinPoint

程序执行过程中可以被拦截的位置。
在 Spring AOP 中,主要指 方法执行

(3)切点 Pointcut

定义“哪些方法需要被增强”。
比如:

execution(* com.demo.service.*.*(..))

表示拦截某个包下所有类的所有方法。

(4)通知 Advice

在连接点执行的增强逻辑。常见通知有:

  • 前置通知 @Before
  • 后置通知 @After
  • 返回通知 @AfterReturning
  • 异常通知 @AfterThrowing
  • 环绕通知 @Around

(5)目标对象 Target

原始业务对象。

(6)代理对象 Proxy

Spring 为目标对象生成的代理对象,调用方拿到的通常是它,而不是原始对象。


4. Spring AOP 的底层实现:动态代理

Spring AOP 不是直接修改原始类,而是为原始对象创建一个代理对象。
外部调用方法时,实际上调用的是代理对象的方法,代理对象会在调用前后织入增强逻辑。

例如:

proxy.save() {
    before();
    target.save();
    after();
}

5. Spring AOP 的两种代理方式

(1)JDK 动态代理

如果目标类实现了接口,Spring 默认优先使用 JDK 动态代理。

特点:

  • 基于接口
  • 底层依赖 InvocationHandler
  • 代理对象本质上是接口实现类

(2)CGLIB 动态代理

如果目标类没有实现接口,Spring 会使用 CGLIB 动态代理。

特点:

  • 基于继承
  • 通过生成目标类的子类实现代理
  • 通过重写方法实现增强

因此它有一些限制:

  • final 类不能代理
  • final 方法不能增强
  • private 方法不能被重写,因此也无法正常增强

6. AOP 的执行流程

Spring AOP 的典型执行流程是:

第一步,Spring 先通过 IoC 创建原始 Bean。
第二步,在 Bean 初始化过程中,Spring 会判断该 Bean 是否匹配切面规则。
第三步,如果匹配,就为它创建代理对象。
第四步,容器最终保存的往往不是原始对象,而是代理对象。
第五步,业务代码调用方法时,先进入代理对象。
第六步,代理对象按照责任链顺序执行各种通知。
第七步,最后才真正调用目标方法。

比如事务场景下:

  • 方法执行前:开启事务
  • 方法执行成功:提交事务
  • 方法抛异常:回滚事务

这也是为什么 @Transactional 能生效,本质就是 AOP 代理在起作用。


三、IoC 与 AOP 的关系

IoC 和 AOP 的关系可以概括为一句话:

  • IoC 负责创建和管理对象
  • AOP 负责增强对象的功能

AOP 不是独立存在的,它是建立在 IoC 之上的。
Spring 先通过 IoC 把 Bean 创建出来,再在初始化阶段判断是否需要 AOP,如果需要,就返回代理对象。

因此可以说:

AOP 是 IoC 容器在 Bean 生命周期中的一个扩展能力。


四、总结

Spring IoC 的核心原理是控制反转和依赖注入。Spring 会读取 XML、注解或配置类中的元数据,将其解析成 BeanDefinition,再通过反射实例化对象并完成依赖注入、初始化和生命周期管理,从而把对象的创建和维护交给容器统一控制。

Spring AOP 的核心原理是动态代理。Spring 在 Bean 创建完成后,会判断该 Bean 是否匹配切面规则,如果匹配,就为它创建代理对象。外部调用方法时,实际调用的是代理对象,代理对象会在目标方法前后织入增强逻辑。若目标类实现了接口,通常使用 JDK 动态代理;否则使用 CGLIB 动态代理。AOP 常用于事务、日志、权限控制等横切逻辑。