一、IoC(Inversion of Control,控制反转)
定义:
IoC(Inversion of Control,控制反转),就是把对象创建和依赖关系的管理交给 Spring 容器,而不是由程序员手动去创建对象和管理依赖。
IoC 以前是我们想要什么就自己创建什么,现在是我们需要什么容器就帮我们送来什么。
举例:
以前:
UserService userService = new UserServiceImpl();
现在:
@Autowired
UserService userService;
Spring 帮忙创建并注入了这个对象,这就叫 控制反转(IoC)。
Spring 倡导的开发方式就是这样,所有类的创建和销毁都通过 Spring 容器来,不再是开发者去 new,去 = null
,这样就实现了对象的解耦。
于是,对于某个对象来说,以前是它控制它依赖的对象,现在是所有对象都被 Spring 控制。
说说什么是 DI?
定义:
IoC 是一种思想,DI 是实现 IoC 的具体方式,将一个对象所依赖的其他对象 以参数的形式传入,由外部容器(如 Spring)来“注入”依赖,而不是对象自己创建。
打个比方,你现在想吃炒菜和饭,点个外卖这时候就有人把炒好的菜和煮好的饭送到你手上。就好像 A 类需要 B 类,以前是 A 类自己 new 一个 B 类,现在是有人把 B 类送给到 A 类里。
常见方式:
- 构造方法注入
- Setter 方法注入
- 字段注入(推荐使用构造注入)
举例(构造注入):
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Spring 会自动将 UserRepository
注入。
为什么要使用 IoC 呢?
- 解耦:让对象的创建和管理交给 Spring 容器,降低对象之间的耦合度。
- 提高可维护性:对象的依赖关系由 Spring 统一管理,方便修改和扩展。
总结
IoC(Inversion of Control)
指的是控制反转,将对象创建和依赖关系都交给 Spring 容器去做;DI(Dependency Injection)
指的是依赖注入,是 IoC 的具体实现方式,包括构造方法、setter 方法和注解方式注入;使用IoC 是为了降低对象间的耦合度和方便修改和扩展。
Spring IoC 容器运行机制的核心之一:Bean 的生命周期
完整生命周期流程图分为五个阶段:
- 实例化:Spring 首先使用构造方法或者工厂方法创建一个 Bean 的实例。在这个阶段,Bean 还没有被依赖注入。
- 属性赋值:Spring 根据配置文件,将所有所需的属性值或依赖的 Bean 注入到该 Bean 中。这个过程称为依赖注入。
- 初始化:在 Bean 依赖注入完成后,Spring 允许 Bean 进行初始化,可以在这里执行一些启动逻辑。(Spring 调用 afterPropertiesSet 方法,或通过配置文件指定的 init-method 方法,完成初始化。)
- 使用中:Bean 准备好可以使用了。
- 销毁:在容器关闭时,Spring 会调用 destroy 方法,完成 Bean 的清理工作。
实例化
↓
属性赋值(依赖注入)
↓
调用 BeanNameAware / BeanFactoryAware 等
↓
调用初始化方法(如 @PostConstruct、afterPropertiesSet())
↓
Bean 准备就绪,可被使用
↓
容器关闭前调用销毁方法(如 @PreDestroy、destroy())
从源码角度来讲:
- 实例化:Spring 容器根据 Bean 的定义创建 Bean 的实例,相当于执行构造方法,也就是 new 一个对象。
- 属性赋值:相当于执行 setter 方法为字段赋值。
- 初始化:初始化阶段允许执行自定义的逻辑,比如设置某些必要的属性值、开启资源、执行预加载操作等,以确保 Bean 在使用之前是完全配置好的。
- 销毁:相当于执行
= null
,释放资源。
可以在源码 AbstractAutowireCapableBeanFactory
中的 doCreateBean
方法中,看到 Bean 的前三个生命周期:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 实例化阶段
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
...
Object exposedObject = bean;
try {
// 属性赋值阶段
this.populateBean(beanName, mbd, instanceWrapper);
// 初始化阶段
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
...
}
...
}
源码位置,见下图:
至于销毁,是在容器关闭的时候调用的,详见 ConfigurableApplicationContext
的 close
方法。
二、AOP(Aspect-Oriented Programming,面向切面编程)
定义:
AOP,也就是面向切面编程,将业务逻辑中一些 通用的功能逻辑(如日志、事务、权限)从业务逻辑中抽离,统一切入到一个独立的模块中,让业务逻辑更加清爽,提高代码复用性和解耦性。
核心概念:
名称 | 含义 |
---|---|
JoinPoint | 程序执行的某个点,如方法调用 |
Pointcut | 切入点,匹配 JoinPoint 的规则 |
Advice | 要执行的逻辑,如 before/after/around |
Aspect | 切面,通知和切入点的组合 |
Weaving | 将切面织入到目标对象的过程 |
举例:
场景:给 Service 方法统一加上日志
我们有一个 UserService
,每次调用它的方法前都想打印日志,比如:
[Log] 正在调用方法:getUserById
1、创建业务类
@Service
public class UserService {
public String getUserById(Long id) {
System.out.println("正在执行 getUserById 业务逻辑");
return "用户ID:" + id;
}
}
2、创建切面类(日志功能)
@Aspect
@Component
public class LogAspect {
// 切点:拦截所有 UserService 的方法
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}
// 前置通知:方法执行前打印日志
@Before("userServiceMethods()")
public void logBefore(JoinPoint joinPoint) {
String method = joinPoint.getSignature().getName();
System.out.println("[Log] 正在调用方法:" + method);
}
// 后置通知:方法执行完后打印
@AfterReturning(pointcut = "userServiceMethods()", returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) {
System.out.println("[Log] 方法执行完成,返回结果:" + result);
}
}
3、开启 AOP 功能(Spring Boot 自动开启)
确保 Spring Boot 项目中依赖了 spring-boot-starter-aop
:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
4、运行效果
调用如下代码:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
}
控制台输出:
[Log] 正在调用方法:getUserById
正在执行 getUserById 业务逻辑
[Log] 方法执行完成,返回结果:用户ID:123
总结一句话:
AOP 是在 不改动源码的情况下 给程序添加额外功能(日志、事务、监控等)。
IoC、DI、AOP 对比:
概念 | 一句话解释 |
---|---|
IoC | 把对象的创建和管理交给 Spring 容器 |
DI | Spring 容器负责把依赖“注入”到类中 |
AOP | 给程序动态添加功能,而不改动源码 |