Spring 最核心的两个能力就是 IoC 和 AOP。
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前置处理@PostConstructInitializingBean.afterPropertiesSet()- 自定义 init-method
BeanPostProcessor后置处理
第六步:放入容器缓存
如果是单例 Bean,会放到单例池中,后续直接复用。
第七步:销毁
容器关闭时,会调用销毁方法,例如:
@PreDestroyDisposableBean.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 常用于事务、日志、权限控制等横切逻辑。