Spring 框架的IOC与AOP
·
Spring 框架的核心是IOC(控制反转)和 AOP(面向切面编程),这两个特性是 Spring 所有功能的基础,也是它能解决 “代码耦合、通用逻辑冗余” 问题的核心。
1. IOC(控制反转)—— 解耦对象依赖
- 核心思想:把对象的创建、依赖组装的权力从业务代码 “反转” 给 Spring 容器,而非由对象自己创建依赖;
- 具体实现:依赖注入(DI)是 IOC 的落地方式(构造器注入、Setter 注入、字段注入);
- 解决问题:传统开发中
UserService service = new UserServiceImpl()这种硬编码创建对象的方式耦合度极高,IOC 让容器统一管理 Bean,替换实现类、调整依赖无需修改业务代码; - 实战举例:项目中所有 Service、Controller 都交给 Spring 容器管理,通过
@Autowired/ 构造器注入依赖,无需手动 new 对象,大幅降低耦合。
依赖注入是 Spring 核心 IOC(控制反转)的具体实现方式,指对象的依赖由 Spring 容器创建并注入,而非自身创建,核心是解耦。理解如下:
- 传统开发中,对象需要自己创建依赖(如
UserService service = new UserServiceImpl()),耦合度高; - 依赖注入下,对象的依赖不再由自身创建,而是由 Spring 容器创建并 “注入” 到对象中;
- 本质是 “将对象的创建权交给容器,容器负责组装对象之间的依赖关系”,最终实现解耦(比如替换 UserService 的实现类,无需修改业务代码)。
依赖注入的 3 种核心方式
1.1. 构造器注入(推荐,Spring 官方首选)
- 原理:通过类的构造方法注入依赖,Spring 会调用带参数的构造器,将容器中的 Bean 传入;
- 优点:依赖不可变(构造后无法修改)、保证依赖非空(必须传入),避免空指针;
- 实战代码:java运行
@Service
public class OrderService {
// 依赖的对象
private final UserService userService;
// 构造器注入(Spring 5+ 可省略 @Autowired)
public OrderService(UserService userService) {
this.userService = userService;
}
}
1.2. Setter 注入
- 原理:通过类的 Setter 方法注入依赖,需配合
@Autowired注解; - 优点:依赖可选(可随时修改),适合非必需的依赖;
- 实战代码:java运行
@Service
public class OrderService {
private UserService userService;
// Setter注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
1.3. 字段注入(便捷但不推荐)
- 原理:直接在类的成员变量上标注
@Autowired,Spring 直接给字段赋值; - 优点:代码简洁、无需写构造器 / Setter;
- 缺点:无法实现依赖不可变(字段可被修改)、单元测试难(需手动注入依赖)、可能导致空指针;
- 实战代码:java运行
@Service
public class OrderService {
// 字段注入
@Autowired
private UserService userService;
}
1.4. 另外:当一个接口有多个实现类时,需指定名称。
例:java运行
// 方式1:@Autowired + @Qualifier
@Autowired
@Qualifier("userServiceImplV2")
private UserService userService;
// 方式2:@Resource
@Resource(name = "userServiceImplV2")
private UserService userService;
2. AOP(面向切面编程)—— 抽离通用逻辑
AOP 是面向切面编程,把日志、权限、事务等通用逻辑抽成 “切面”,动态织入业务方法,不侵入业务代码,核心是解耦通用逻辑和业务逻辑。
- 核心思想:将与业务无关的通用逻辑(日志、权限、事务、性能监控)抽成 “切面”,在不修改业务代码的前提下,动态织入到目标方法的指定位置(如方法执行前 / 后);
- 解决问题:避免通用逻辑散落在各个业务方法中(如每个接口都写日志代码),实现 “一处定义、处处复用”;
- 实战举例:用
@Transactional注解实现声明式事务(底层是 AOP),用自定义切面实现接口统一日志打印,无需在每个 Controller 方法中写日志代码。
AOP 的底层是动态代理:
2.1、JDK 动态代理:被代理类实现接口时,用 JDK 动态代理(基于接口生成代理类);
2.1.1 适用场景:被代理类实现了接口(如 Controller、Service 实现接口);
2.1.2 核心原理:通过 java.lang.reflect.Proxy 类的 newProxyInstance() 方法,动态生成一个实现了目标接口的代理类;代理类持有 InvocationHandler 接口实现类,在调用目标方法时,先执行 InvocationHandler 的 invoke() 方法(切面逻辑),再调用目标方法;
2.1.3 特点:基于接口,无需第三方依赖,JDK 原生支持。
2.2、CGLIB 动态代理:未实现接口时,用 CGLIB 动态代理(基于子类生成代理类);
2.1 适用场景:被代理类没有实现接口(如自定义的工具类);
2.2 核心原理:通过 ASM 字节码框架,动态生成目标类的子类作为代理类;重写目标方法,在重写的方法中先执行切面逻辑,再调用父类的目标方法;
2.3 注意:由于是子类代理,目标方法不能是 final(final 方法无法被重写)。
2.3、Spring AOP 在运行时生成代理对象,调用目标方法时先执行切面逻辑,再执行原方法,实现无侵入增强。Spring AOP 的织入流程如下:(核心步骤)
- 2.3.1 扫描切面:Spring 启动时,扫描标有
@Aspect的切面类,解析@Pointcut切点和@Before/@After等通知; - 2.3.2 创建代理对象:对匹配切点的目标 Bean,Spring 不直接返回原 Bean,而是返回动态代理对象;
- 2.3.3 方法调用:调用目标方法时,实际调用的是代理对象的方法,代理对象先执行切面逻辑(如日志、权限),再调用原目标方法;
- 2.3.4 织入时机:Spring AOP 是运行时织入(区别于编译期织入、类加载期织入),只有在方法调用时才触发代理逻辑。
2.4、 Spring Boot 使用 AOP
- 2.4.1 引入spring-boot-starter-aop依赖;
- 2.4.2 编写切面类,用@Aspect + @Component标记;
- 2.4.3 用@Pointcut定义切点(匹配指定方法 / 注解),用@Before/@After等定义通知;
- 2.4.4 项目中常用 AOP 做接口日志、权限校验、接口耗时监控,大幅减少重复代码。
3. 补充
IOC 和 AOP 是基础,在此之上 Spring 扩展出其他核心能力:
- 事务管理:基于 AOP 实现声明式事务,简化
begin/commit/rollback操作; - Bean 生命周期管理:容器负责 Bean 的创建、初始化、销毁,支持自定义初始化 / 销毁逻辑;
- 资源整合:统一整合 MyBatis、Redis、MQ 等第三方框架,通过 IOC 管理这些框架的组件。
总结
- Spring的核心双支柱:IOC 解决对象依赖耦合,AOP 解决通用逻辑冗余;
- 落地价值:Spring 所有高级特性(事务、缓存、异步)都基于 IOC/AOP 实现;
更多推荐




所有评论(0)