【Spring】两大核心基石 IoC和 AOP

Spring 框架的两大核心基石:IoC(控制反转)AOP(面向切面编程) 理解这两者是如何协同工作的,是掌握 Spring 精髓的关键

一、Spring IoC(控制反转)

1. 核心思想:观念的转变
传统方式(正转) :在普通程序中,对象 A 如果需要使用对象 B,会由对象 A 主动​ 通过 new关键字来创建对象 B。即,程序的控制权在对象 A

手中。

java 复制代码
public class OrderService {
    // 主动创建依赖
    private UserDao userDao = new UserDaoImpl();
}

IoC 方式(反转):控制权被"反转"了。对象 A 不再主动创建对象 B,而是被动等待。由一个外部的容器(Spring IoC 容器)来负责对象的创建、组装和管理。当需要时,容器会将对象 B"注入"到对象 A 中。

java 复制代码
public class OrderService {
    // 被动接收依赖
    @Autowired
    private UserDao userDao; // 容器会负责把 UserDaoImpl 的实例注入到这里
}

2. 实现方式:DI(依赖注入)

DI 是 IoC 思想的一种具体实现方式。所谓"依赖注入",就是由 IoC 容器在运行期,动态地将某个对象所依赖的其他对象(即它的属性或参数)注入给它
主要的注入方式:
构造器注入(推荐):通过构造函数传递依赖。

java 复制代码
@Component
public class OrderService {
    private final UserDao userDao;

    // 构造器注入
    @Autowired // Spring 4.3 后,如果只有一个构造器,可省略 @Autowired
    public OrderService(UserDao userDao) {
        this.userDao = userDao;
    }
}

Setter 方法注入:通过 Setter 方法设置依赖

java 复制代码
@Component
public class OrderService {
    private UserDao userDao;

    // Setter 注入
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

字段注入(不推荐):通过 @Autowired直接标注在字段上。这种方式不利于测试和不变性

java 复制代码
@Component
public class OrderService {
    @Autowired // 字段注入(不推荐)
    private UserDao userDao;
}

3. IoC 容器

Spring IoC 容器的核心是 BeanFactory接口,而 ApplicationContext是其更强大的子接口,提供了更多企业级功能(如国际化、事件发布等)。容器负责:
实例化 ​ Bean
配置 ​ Bean(如注入依赖)
组装 ​ Bean 之间的关系
管理​ Bean 的整个生命周期

二、Spring AOP(面向切面编程)

1. 核心思想:分离关注点

在业务系统中,除了核心业务逻辑(如创建订单、查询用户),还存在大量横切关注点,例如:日志记录、事务管理、安全校验、性能监控

这些代码如果直接写在业务方法中,会导致:
代码冗余 :每个业务方法都要写一遍。
代码混乱 :核心业务逻辑被非核心代码淹没。
难以维护 :修改日志或事务策略时,需要改动所有相关方法

AOP 的解决方案是:将这些横切关注点模块化为"切面",然后通过声明的方式,定义这些切面应该在何处、何时被应用。​ 从而使得业务类只关注核心逻辑。

2. AOP 关键术语
Aspect(切面) :一个横切关注点的模块化。就是一个用 @Aspect注解的类,里面包含了通知和切点。
Advice(通知) :切面在特定连接点上执行的动作。例如"日志记录"这个动作。类型有:

@Before:在方法执行前通知。

@AfterReturning:在方法成功执行后通知。

@AfterThrowing:在方法抛出异常后通知。

@After(finally):在方法执行后(无论成功与否)通知。

@Around:最强大的通知,环绕方法执行,可以控制是否执行目标方法。
Pointcut(切点) :一个表达式,用于匹配哪些类的哪些方法需要被增强。它定义了通知(Advice)被应用的"位置"。
Join Point(连接点) :程序执行过程中可以插入切面的点。在 Spring AOP 中,这总是代表一个方法的执行。
Weaving(织入):将切面应用到目标对象,从而创建代理对象的过程。Spring AOP 在运行时通过动态代理完成织入

3. 代码示例

假设我们想在所有 Service 层方法执行前后记录日志。
①定义切面(Aspect):

java 复制代码
@Aspect
@Component
public class LoggingAspect {

    // 定义切点:匹配 com.example.service 包下所有类的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    // 定义前置通知
    @Before("serviceLayer()")
    public void logBeforeMethod(JoinPoint joinPoint) {
        System.out.println("准备执行方法: " + joinPoint.getSignature().getName());
    }

    // 定义环绕通知(功能最全)
    @Around("serviceLayer()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        long elapsedTime = System.currentTimeMillis() - start;
        System.out.println(joinPoint.getSignature() + " 执行耗时: " + elapsedTime + "ms");
        
        return result;
    }
}

② 业务类(完全不知道日志的存在):

java 复制代码
@Service
public class UserService {
    public User findUserById(Long id) {
        // ... 纯业务逻辑,没有日志代码
        return user;
    }
}

当调用 userService.findUserById(1L)时,控制台会输出:

java 复制代码
准备执行方法: findUserById
User findUserById 执行耗时: 15ms

三、IoC 与 AOP 的协同工作:珠联璧合

下图清晰地展示了 Spring 如何将 IoC 和 AOP 完美结合,共同构建应用程序:

1.IoC 是基础 :IoC 容器负责创建和管理所有对象(Bean),包括你的业务核心类(如 UserService)和切面类(如 LoggingAspect)。
2.AOP 是增强 :在 IoC 容器完成 Bean 的依赖注入后、初始化之前(具体是在 BeanPostProcessor后置处理阶段),容器会检查是否有切面与当前 Bean 匹配。
3.动态代理 :如果匹配,Spring 不会将原始的 UserService实例直接交给其他组件。相反,它会创建一个代理对象。这个代理对象包装了原始对象。
4.协同工作 :当你的 OrderController(由 IoC 容器注入 UserService时,它实际得到的是这个代理对象,而非原始对象。
5.最终效果 :当调用 userService.findUserById()时,调用首先会进入代理对象。代理对象先执行切面逻辑(如日志记录),然后再将调用委托给原始的 UserService对象执行核心业务逻辑。这样,IoC 保证了对象的组装,而 AOP 实现了无侵入式的功能增强。
总结

简单来说:IoC 让对象之间的依赖关系变得清晰、可管理;而 AOP 则让这些对象能够"免费"获得像日志、事务这样的通用能力,而无需污染自己的代码。​ 二者结合,使得 Spring 能够构建出高度解耦、可维护、可扩展的企业级应用程序。

相关推荐
cike_y13 小时前
Mybatis之解析配置优化
java·开发语言·tomcat·mybatis·安全开发
是一个Bug14 小时前
Java基础50道经典面试题(四)
java·windows·python
Slow菜鸟14 小时前
Java基础架构设计(三)| 通用响应与异常处理(分布式应用通用方案)
java·开发语言
我是Superman丶15 小时前
《Spring WebFlux 实战:基于 SSE 实现多类型事件流(支持聊天消息、元数据与控制指令混合传输)》
java
廋到被风吹走15 小时前
【Spring】常用注解分类整理
java·后端·spring
是一个Bug15 小时前
Java基础20道经典面试题(二)
java·开发语言
Z_Easen15 小时前
Spring 之元编程
java·开发语言
leoufung15 小时前
LeetCode 373. Find K Pairs with Smallest Sums:从暴力到堆优化的完整思路与踩坑
java·算法·leetcode
阿蒙Amon15 小时前
C#每日面试题-委托和事件的区别
java·开发语言·c#
宋情写15 小时前
java-IDEA
java·ide·intellij-idea