跳到主要内容

Spring IOC 详解

IOC(Inversion of Control,控制反转)是Spring框架的核心概念,它通过依赖注入(DI)实现了对象创建和依赖关系的管理,让开发者专注于业务逻辑的实现。

核心价值

IOC = 控制反转 + 依赖注入 + 对象管理 + 松耦合设计

  • 🔄 控制反转:将对象的创建和管理权交给容器
  • 💉 依赖注入:通过构造函数、setter或字段自动注入依赖
  • 🧩 对象管理:统一管理Bean的生命周期和作用域
  • 🔗 松耦合设计:降低组件间的耦合度,提高可维护性

1. IOC基础概念

1.1 什么是IOC?

IOC是一种设计模式,它将对象的创建和依赖关系的管理从代码中分离出来,交给容器来处理。这样可以降低代码的耦合度,提高代码的可维护性和可测试性。

传统方式 vs IOC方式

java
1// 传统方式 - 紧耦合
2public class UserService {
3 private UserRepository userRepository;
4
5 public UserService() {
6 // 直接创建依赖对象
7 this.userRepository = new UserRepositoryImpl();
8 }
9
10 public void createUser(User user) {
11 userRepository.save(user);
12 }
13}

1.2 IOC容器

Spring IOC容器负责管理对象的生命周期和依赖关系。Spring提供了两种容器类型:BeanFactory和ApplicationContext。

特性BeanFactoryApplicationContext
加载方式懒加载预加载
国际化不支持支持
事件发布不支持支持
资源访问有限支持完全支持
适用场景资源受限环境大多数应用
IOC容器示例
java
1// 创建IOC容器
2ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
3
4// 从容器获取Bean
5UserService userService = context.getBean(UserService.class);
6
7// 使用Bean
8userService.createUser(new User("张三"));

2. 依赖注入方式

Spring提供多种依赖注入方式,每种方式都有其适用场景。

2.1 构造器注入

构造器注入
java
1@Service
2public class UserService {
3 private final UserRepository userRepository;
4 private final EmailService emailService;
5
6 // 构造器注入
7 public UserService(UserRepository userRepository, EmailService emailService) {
8 this.userRepository = userRepository;
9 this.emailService = emailService;
10 }
11
12 public void createUser(User user) {
13 userRepository.save(user);
14 emailService.sendWelcomeEmail(user.getEmail());
15 }
16}

2.2 Setter注入

Setter注入
java
1@Service
2public class UserService {
3 private UserRepository userRepository;
4 private EmailService emailService;
5
6 // Setter注入
7 @Autowired
8 public void setUserRepository(UserRepository userRepository) {
9 this.userRepository = userRepository;
10 }
11
12 @Autowired
13 public void setEmailService(EmailService emailService) {
14 this.emailService = emailService;
15 }
16}

2.3 字段注入

字段注入
java
1@Service
2public class UserService {
3 @Autowired
4 private UserRepository userRepository;
5
6 @Autowired
7 private EmailService emailService;
8
9 public void createUser(User user) {
10 userRepository.save(user);
11 emailService.sendWelcomeEmail(user.getEmail());
12 }
13}
注入方式选择
  • 构造器注入:推荐使用,可以确保依赖不为null,支持不可变对象
  • Setter注入:适用于可选依赖,当依赖有合理默认值时
  • 字段注入:简单但不推荐,不利于测试和不可变性,隐藏依赖关系

3. Bean生命周期

3.1 Bean生命周期阶段

Spring Bean的生命周期包含多个阶段,从实例化到销毁,容器会在各个阶段提供回调机制。

完整生命周期示例代码
Bean生命周期示例
java
1@Component
2public class UserService implements InitializingBean, DisposableBean {
3
4 public UserService() {
5 System.out.println("1. 构造函数 - 实例化");
6 }
7
8 @Autowired
9 public void setUserRepository(UserRepository userRepository) {
10 System.out.println("2. 设置属性 - 依赖注入");
11 }
12
13 @PostConstruct
14 public void init() {
15 System.out.println("3. @PostConstruct - 初始化");
16 }
17
18 @Override
19 public void afterPropertiesSet() throws Exception {
20 System.out.println("4. InitializingBean.afterPropertiesSet()");
21 }
22
23 public void customInit() {
24 System.out.println("5. 自定义初始化方法");
25 }
26
27 public void businessMethod() {
28 System.out.println("6. 业务方法 - 使用Bean");
29 }
30
31 @PreDestroy
32 public void preDestroy() {
33 System.out.println("7. @PreDestroy - 销毁前");
34 }
35
36 @Override
37 public void destroy() throws Exception {
38 System.out.println("8. DisposableBean.destroy()");
39 }
40
41 public void customDestroy() {
42 System.out.println("9. 自定义销毁方法");
43 }
44}

3.2 生命周期回调

阶段回调方法说明
实例化构造器创建Bean实例
属性注入setter方法注入依赖属性
初始化前BeanPostProcessor前置处理器
初始化@PostConstruct注解方式初始化
初始化InitializingBean接口方式初始化
初始化自定义init方法XML或注解配置
初始化后BeanPostProcessor后置处理器
使用业务方法Bean正常使用
销毁前@PreDestroy注解方式销毁前
销毁DisposableBean接口方式销毁
销毁自定义destroy方法XML或注解配置

4. Bean作用域

4.1 作用域类型

Spring提供了多种Bean作用域,用于控制Bean实例的生命周期和可见性。

单例作用域
java
1@Component
2@Scope("singleton") // 默认作用域,可以省略
3public class SingletonService {
4 // 整个容器只有一个实例
5
6 public SingletonService() {
7 System.out.println("创建SingletonService");
8 }
9
10 public void doSomething() {
11 System.out.println("SingletonService doing something");
12 }
13}

4.2 作用域特点

作用域说明特点使用场景
singleton单例整个容器只有一个实例无状态服务、配置类
prototype原型每次获取都创建新实例有状态对象、线程安全
request请求每个HTTP请求一个实例Web请求处理器
session会话每个HTTP会话一个实例用户会话状态
application应用每个ServletContext一个实例全局Web资源
websocket连接每个WebSocket连接一个实例WebSocket处理器
作用域注意事项

当单例Bean依赖非单例Bean时,需要使用代理模式,否则非单例Bean将失去其作用域特性。使用proxyMode = ScopedProxyMode.TARGET_CLASS来解决此问题。

5. 配置方式

Spring提供了三种主要的Bean配置方式,可以根据需求选择合适的方式。

5.1 注解配置

组件扫描
java
1@Configuration
2@ComponentScan("com.example")
3public class AppConfig {
4 // 配置类
5}
6
7@Service
8public class UserService {
9 @Autowired
10 private UserRepository userRepository;
11}
12
13@Repository
14public class UserRepositoryImpl implements UserRepository {
15 // 实现
16}

5.2 XML配置

XML配置示例
XML配置
xml
1<?xml version="1.0" encoding="UTF-8"?>
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans.xsd">
6
7 <bean id="userRepository" class="com.example.repository.UserRepositoryImpl"/>
8
9 <bean id="emailService" class="com.example.service.EmailServiceImpl"/>
10
11 <bean id="userService" class="com.example.service.UserService">
12 <constructor-arg ref="userRepository"/>
13 <constructor-arg ref="emailService"/>
14 </bean>
15
16</beans>

5.3 Java配置

Java配置
java
1@Configuration
2public class AppConfig {
3
4 @Bean
5 public UserRepository userRepository() {
6 return new UserRepositoryImpl();
7 }
8
9 @Bean
10 public EmailService emailService() {
11 return new EmailServiceImpl();
12 }
13
14 @Bean
15 public UserService userService(UserRepository userRepository, EmailService emailService) {
16 return new UserService(userRepository, emailService);
17 }
18}

6. 高级特性

6.1 条件化Bean

Spring提供了条件化配置机制,允许根据特定条件创建Bean。

条件化Bean示例
条件化Bean
java
1@Configuration
2public class AppConfig {
3
4 @Bean
5 @ConditionalOnProperty(name = "database.type", havingValue = "mysql")
6 public DataSource mysqlDataSource() {
7 return new MysqlDataSource();
8 }
9
10 @Bean
11 @ConditionalOnProperty(name = "database.type", havingValue = "postgresql")
12 public DataSource postgresqlDataSource() {
13 return new PostgresqlDataSource();
14 }
15
16 @Bean
17 @ConditionalOnClass(name = "com.mysql.jdbc.Driver")
18 public JdbcTemplate jdbcTemplate(DataSource dataSource) {
19 return new JdbcTemplate(dataSource);
20 }
21}

常用条件注解

条件注解说明用途
@ConditionalOnBean当存在指定Bean时依赖其他Bean
@ConditionalOnMissingBean当不存在指定Bean时默认配置
@ConditionalOnClass当存在指定类时依赖外部库
@ConditionalOnMissingClass当不存在指定类时兼容性配置
@ConditionalOnProperty当存在指定属性时配置驱动
@ConditionalOnExpression当表达式为true时复杂条件判断

6.2 Bean后处理器

Bean后处理器允许在Bean初始化前后进行自定义处理。

Bean后处理器
java
1@Component
2public class CustomBeanPostProcessor implements BeanPostProcessor {
3
4 @Override
5 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
6 System.out.println("Bean初始化前: " + beanName);
7 return bean;
8 }
9
10 @Override
11 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
12 System.out.println("Bean初始化后: " + beanName);
13 return bean;
14 }
15}

6.3 工厂Bean

工厂Bean用于创建复杂的Bean实例,隐藏实例化细节。

工厂Bean
java
1@Component
2public class UserServiceFactoryBean implements FactoryBean<UserService> {
3
4 @Override
5 public UserService getObject() throws Exception {
6 // 创建复杂的UserService实例
7 UserService userService = new UserService();
8 // 进行一些复杂的初始化
9 return userService;
10 }
11
12 @Override
13 public Class<?> getObjectType() {
14 return UserService.class;
15 }
16
17 @Override
18 public boolean isSingleton() {
19 return true;
20 }
21}

6.4 循环依赖

Spring通过三级缓存解决单例Bean的循环依赖问题。

循环依赖示例与解决过程
循环依赖示例
java
1@Service
2public class ServiceA {
3 @Autowired
4 private ServiceB serviceB;
5
6 public void methodA() {
7 serviceB.methodB();
8 }
9}
10
11@Service
12public class ServiceB {
13 @Autowired
14 private ServiceA serviceA;
15
16 public void methodB() {
17 serviceA.methodA();
18 }
19}

解决过程:

  1. 创建ServiceA实例
  2. 发现依赖ServiceB,将ServiceA放入三级缓存
  3. 创建ServiceB实例
  4. 发现依赖ServiceA,尝试从缓存获取
  5. 从三级缓存获取ServiceA的早期引用
  6. 完成ServiceB的依赖注入
  7. 完成ServiceA的依赖注入

7. 面试题精选

7.1 基础概念题

Q: 什么是IOC?它的优势是什么?

A: IOC(控制反转)是一种设计模式,它将对象的创建和依赖关系的管理从代码中分离出来。优势包括:

  • 降低耦合度:对象之间的依赖关系由容器管理
  • 提高可维护性:修改依赖关系不需要修改业务代码
  • 提高可测试性:可以轻松替换依赖进行测试
  • 简化代码:专注于业务逻辑实现

7.2 实践题

Q: Spring中有哪些依赖注入方式?

A: Spring提供三种依赖注入方式:

  • 构造器注入:通过构造器注入依赖,推荐使用
  • Setter注入:通过setter方法注入依赖
  • 字段注入:直接在字段上使用@Autowired注解

7.3 高级题

Q: Spring Bean的生命周期是怎样的?

A: Spring Bean的生命周期包括:

  1. 实例化:创建Bean实例
  2. 属性注入:注入依赖属性
  3. BeanPostProcessor前置处理:在初始化前进行处理
  4. 初始化回调:执行初始化回调方法(@PostConstruct、InitializingBean等)
  5. BeanPostProcessor后置处理:在初始化后进行处理
  6. 使用:Bean正常使用
  7. 销毁回调:执行销毁回调方法(@PreDestroy、DisposableBean等)
IOC学习要点
  1. 理解核心概念:掌握IOC、DI、Bean等基本概念
  2. 熟悉注入方式:学会构造器、Setter、字段注入
  3. 掌握生命周期:了解Bean的创建、初始化、销毁过程
  4. 学会配置方式:掌握注解、XML、Java配置
  5. 了解高级特性:学会条件化Bean、后处理器等

通过本章的学习,你应该已经掌握了Spring IOC的核心概念、依赖注入方式和Bean生命周期管理。IOC是Spring框架的基础,掌握IOC对于理解整个Spring生态系统至关重要。在实际项目中,合理使用IOC可以构建出松耦合、易维护的应用程序。

评论