01.01 Spring核心|IoC容器深度解析
导读
- 目标:深入理解Spring IoC容器的核心机制,包括Bean定义、注册、生命周期、作用域等,掌握Spring的设计思想和实现原理。
- 适用场景:Spring框架学习、源码阅读、面试准备、架构设计。
- 前置知识:Java反射、设计模式(工厂模式、单例模式)
一、IoC核心概念
1.1 控制反转(Inversion of Control)
什么是控制反转?
控制反转(IoC)是一种设计原则,它将对象的创建、依赖关系的管理从应用程序代码中转移到框架或容器中。传统编程中,对象自己控制依赖的创建,而IoC让容器来控制这些依赖。
传统方式 vs IoC方式:
java
// 传统方式(紧耦合)
public class UserService {
// 问题1:硬编码依赖,无法替换实现
private UserRepository userRepository = new UserRepositoryImpl();
// 问题2:难以测试,无法mock依赖
public void saveUser(User user) {
userRepository.save(user);
}
}
// IoC方式(松耦合)
public class UserService {
// 依赖由容器注入,可以轻松替换实现
private final UserRepository userRepository;
// 构造函数注入(推荐)
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 或者使用setter注入
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 或者使用字段注入(不推荐,但常见)
@Autowired
private UserRepository userRepository;
}
IoC的优势:
- 解耦:对象不直接依赖具体实现,而是依赖接口
- 可测试:可以轻松注入mock对象进行单元测试
- 可维护:修改实现类不影响使用方
- 灵活性:运行时决定使用哪个实现
实战案例:订单服务
java
// 接口定义
public interface PaymentService {
boolean pay(Order order);
}
// 支付宝实现
@Component
public class AlipayService implements PaymentService {
@Override
public boolean pay(Order order) {
// 支付宝支付逻辑
return true;
}
}
// 微信支付实现
@Component
public class WechatPayService implements PaymentService {
@Override
public boolean pay(Order order) {
// 微信支付逻辑
return true;
}
}
// 订单服务(使用IoC)
@Service
public class OrderService {
private final PaymentService paymentService;
// 构造函数注入,Spring会自动选择合适的实现
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void processOrder(Order order) {
// 业务逻辑
paymentService.pay(order);
}
}
1.2 Bean容器
容器层次结构:
Spring提供了两个核心接口:BeanFactory 和 ApplicationContext。
BeanFactory(基础容器接口)
├─ 延迟加载Bean
├─ 按需创建Bean
└─ 轻量级容器
ApplicationContext(应用上下文,继承BeanFactory)
├─ 容器启动时创建所有单例Bean
├─ 国际化支持(MessageSource)
├─ 事件发布(ApplicationEventPublisher)
├─ 资源访问(ResourceLoader)
└─ 自动注册BeanPostProcessor、BeanFactoryPostProcessor
├─ ConfigurableApplicationContext
│ ├─ AbstractApplicationContext
│ │ ├─ GenericApplicationContext
│ │ ├─ AnnotationConfigApplicationContext(注解配置)
│ │ └─ ClassPathXmlApplicationContext(XML配置)
│ └─ WebApplicationContext(Web环境)
│ └─ AnnotationConfigWebApplicationContext
BeanFactory vs ApplicationContext 对比:
| 特性 | BeanFactory | ApplicationContext |
|---|---|---|
| Bean加载时机 | 延迟加载(Lazy) | 启动时加载所有单例Bean |
| 国际化支持 | ❌ | ✅ |
| 事件发布 | ❌ | ✅ |
| 自动注册后处理器 | ❌ | ✅ |
| 性能 | 更快启动 | 启动稍慢,但运行时更快 |
| 使用场景 | 资源受限环境 | 大多数应用(推荐) |
容器初始化方式:
java
// 方式1:基于XML配置(传统方式,现在较少使用)
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean(UserService.class);
// 方式2:基于注解配置(Spring Boot默认方式)
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 方式3:Spring Boot自动配置(最常用)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
源码分析:容器启动流程
java
// AbstractApplicationContext.refresh() 方法核心流程
public void refresh() throws BeansException {
// 1. 准备刷新上下文
prepareRefresh();
// 2. 获取BeanFactory并加载Bean定义
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory(设置类加载器、后处理器等)
prepareBeanFactory(beanFactory);
try {
// 4. 允许后处理BeanFactory
postProcessBeanFactory(beanFactory);
// 5. 调用BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化MessageSource(国际化)
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 初始化特殊Bean(如Web容器)
onRefresh();
// 10. 注册监听器
registerListeners();
// 11. 实例化所有单例Bean(核心步骤)
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新
finishRefresh();
} catch (BeansException ex) {
// 清理资源
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
1.3 Bean定义与注册
Bean定义方式对比:
Spring提供了三种方式定义Bean,各有适用场景:
java
// 方式1:XML配置(传统方式,现在较少使用)
// applicationContext.xml
<bean id="userService" class="com.example.UserService">
<property name="userRepository" ref="userRepository"/>
</bean>
// 方式2:注解配置(最常用,Spring Boot默认)
@Component // 或 @Service、@Repository、@Controller
public class UserService {
@Autowired
private UserRepository userRepository;
}
// 方式3:Java配置(灵活,适合复杂配置)
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new UserRepositoryImpl();
}
@Bean
public UserService userService(UserRepository userRepository) {
// 方法参数会自动注入
return new UserService(userRepository);
}
}
Bean注册流程详解:
java
// Spring容器启动完整流程
┌─────────────────────────────────────────────────┐
│ 1. 加载配置源 │
│ - XML文件、注解类、Java配置类 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 2. 解析Bean定义(BeanDefinition) │
│ - 解析XML/注解,生成BeanDefinition对象 │
│ - BeanDefinition包含:类名、作用域、依赖等 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 3. 注册BeanDefinition到BeanFactory │
│ - DefaultListableBeanFactory.registerBeanDefinition() │
│ - 存储到beanDefinitionMap中 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 4. 调用BeanFactoryPostProcessor │
│ - 可以修改BeanDefinition(如PropertyPlaceholderConfigurer)│
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 5. 实例化Bean(单例模式,非延迟加载) │
│ - AbstractBeanFactory.getBean() │
│ - 调用构造函数创建实例 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 6. 属性赋值(依赖注入) │
│ - 通过反射设置@Autowired字段 │
│ - 调用setter方法 │
│ - 构造函数参数注入 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 7. 初始化前处理 │
│ - BeanPostProcessor.postProcessBeforeInitialization() │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 8. 初始化 │
│ - @PostConstruct方法 │
│ - InitializingBean.afterPropertiesSet() │
│ - init-method │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 9. 初始化后处理 │
│ - BeanPostProcessor.postProcessAfterInitialization() │
│ - AOP代理在此创建 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 10. Bean可以使用 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 11. 容器关闭时销毁 │
│ - @PreDestroy方法 │
│ - DisposableBean.destroy() │
│ - destroy-method │
└─────────────────────────────────────────────────┘
BeanDefinition源码分析:
java
// BeanDefinition接口定义了Bean的元数据
public interface BeanDefinition {
// Bean的类名
void setBeanClassName(String beanClassName);
// Bean的作用域
void setScope(String scope);
// 是否单例
boolean isSingleton();
// 是否原型
boolean isPrototype();
// 是否延迟加载
void setLazyInit(boolean lazyInit);
// 依赖的Bean名称
void setDependsOn(String... dependsOn);
// 工厂方法名
void setFactoryMethodName(String factoryMethodName);
}
1.4 Bean作用域
Spring支持的Bean作用域:
| 作用域 | 说明 | 使用场景 | 线程安全 |
|---|---|---|---|
| singleton(默认) | 容器中只有一个实例 | 无状态服务、工具类 | 需保证线程安全 |
| prototype | 每次获取都创建新实例 | 有状态对象、需要隔离的场景 | 天然线程安全 |
| request | 每个HTTP请求一个实例 | Web环境,请求级别的数据 | 天然线程安全 |
| session | 每个HTTP Session一个实例 | Web环境,用户会话数据 | 天然线程安全 |
| application | ServletContext级别 | Web环境,应用级单例 | 需保证线程安全 |
| websocket | WebSocket会话级别 | WebSocket连接 | 天然线程安全 |
详细说明与实战案例:
java
// 1. Singleton(单例,默认)- 最常用
@Service
@Scope("singleton") // 可以省略,默认就是singleton
public class UserService {
// 容器中只有一个实例,所有地方共享
// 优点:节省内存,性能好
// 缺点:必须保证线程安全
private int counter = 0; // 危险!多线程不安全
public void increment() {
counter++; // 需要加锁或使用AtomicInteger
}
}
// 2. Prototype(原型)- 每次创建新实例
@Component
@Scope("prototype")
public class TaskProcessor {
// 每次getBean()都创建新实例
// 适用场景:有状态对象、需要隔离处理
private String taskId;
public void process(String taskId) {
this.taskId = taskId;
// 处理任务
}
}
// 使用示例
@Autowired
private ApplicationContext applicationContext;
public void processTasks() {
// 每次获取都是新实例
TaskProcessor processor1 = applicationContext.getBean(TaskProcessor.class);
TaskProcessor processor2 = applicationContext.getBean(TaskProcessor.class);
// processor1 != processor2
}
// 3. Request(Web环境)- 每个HTTP请求一个实例
@Component
@Scope("request")
public class RequestContext {
// 每个HTTP请求都有独立的实例
// 适用场景:存储请求级别的数据
private String requestId;
private Long userId;
public void setRequestId(String requestId) {
this.requestId = requestId;
}
}
// 4. Session(Web环境)- 每个Session一个实例
@Component
@Scope("session")
public class UserSession {
// 每个用户Session都有独立的实例
// 适用场景:存储用户会话数据
private String username;
private List<String> permissions;
}
// 5. 自定义作用域(高级用法)
@Component
@Scope("thread") // 需要自定义Scope实现
public class ThreadLocalContext {
// 每个线程一个实例
}
作用域选择建议:
- 默认使用singleton:大多数Bean应该是无状态的单例
- 有状态对象用prototype:如果对象有可变状态且需要隔离
- Web相关用request/session:存储请求或会话级别的数据
- 注意线程安全:singleton Bean必须保证线程安全
1.5 Bean生命周期
完整的Bean生命周期(详细版):
java
┌─────────────────────────────────────────────────────┐
│ 阶段1:实例化(Instantiation) │
│ - 调用构造函数创建Bean实例 │
│ - 此时Bean还未完全初始化,不能使用 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 阶段2:属性赋值(Populate Properties) │
│ - 依赖注入:@Autowired、@Resource、@Value │
│ - 设置Bean属性值 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 阶段3:初始化前(Before Initialization) │
│ - BeanPostProcessor.postProcessBeforeInitialization() │
│ - 可以修改Bean实例(如AOP代理) │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 阶段4:初始化(Initialization) │
│ 执行顺序: │
│ 1. @PostConstruct方法(优先级最高) │
│ 2. InitializingBean.afterPropertiesSet() │
│ 3. init-method(XML配置或@Bean(initMethod)) │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 阶段5:初始化后(After Initialization) │
│ - BeanPostProcessor.postProcessAfterInitialization() │
│ - AOP代理通常在此阶段创建 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 阶段6:Bean就绪,可以使用 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 阶段7:容器关闭,销毁前(Before Destruction) │
│ - @PreDestroy方法 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 阶段8:销毁(Destruction) │
│ 执行顺序: │
│ 1. @PreDestroy方法 │
│ 2. DisposableBean.destroy() │
│ 3. destroy-method(XML配置或@Bean(destroyMethod))│
└─────────────────────────────────────────────────────┘
完整生命周期示例:
java
@Component
public class UserService implements InitializingBean, DisposableBean {
@Autowired
private UserRepository userRepository;
// 构造函数(阶段1)
public UserService() {
System.out.println("1. 构造函数执行");
}
// 依赖注入(阶段2)
@Autowired
public void setUserRepository(UserRepository userRepository) {
System.out.println("2. 依赖注入: " + userRepository);
this.userRepository = userRepository;
}
// 初始化前(阶段3)- 通过BeanPostProcessor
// 需要自定义BeanPostProcessor
// 初始化(阶段4)
@PostConstruct
public void postConstruct() {
System.out.println("4.1 @PostConstruct执行");
}
@Override
public void afterPropertiesSet() {
System.out.println("4.2 InitializingBean.afterPropertiesSet()执行");
}
// 初始化后(阶段5)- 通过BeanPostProcessor
// 业务方法
public void doSomething() {
System.out.println("6. Bean使用中");
}
// 销毁前(阶段7)
@PreDestroy
public void preDestroy() {
System.out.println("7. @PreDestroy执行");
}
// 销毁(阶段8)
@Override
public void destroy() {
System.out.println("8. DisposableBean.destroy()执行");
}
}
// 自定义BeanPostProcessor示例
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof UserService) {
System.out.println("3. BeanPostProcessor.postProcessBeforeInitialization()执行");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof UserService) {
System.out.println("5. BeanPostProcessor.postProcessAfterInitialization()执行");
}
return bean;
}
}
生命周期回调方法选择:
| 方法 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| @PostConstruct/@PreDestroy | 标准JSR-250,不依赖Spring | 需要JSR-250支持 | ⭐⭐⭐⭐⭐ |
| InitializingBean/DisposableBean | Spring原生接口 | 耦合Spring框架 | ⭐⭐⭐ |
| init-method/destroy-method | 灵活,可配置 | 需要额外配置 | ⭐⭐⭐⭐ |
实战案例:资源初始化与清理:
java
@Service
public class DatabaseConnectionPool implements InitializingBean, DisposableBean {
private ConnectionPool pool;
@PostConstruct
public void init() {
System.out.println("初始化数据库连接池");
// 初始化连接池
pool = new ConnectionPool();
pool.init();
}
@Override
public void afterPropertiesSet() {
// 验证连接池是否正常
if (!pool.isHealthy()) {
throw new IllegalStateException("连接池初始化失败");
}
}
@PreDestroy
public void cleanup() {
System.out.println("开始清理资源");
}
@Override
public void destroy() {
if (pool != null) {
pool.close();
System.out.println("连接池已关闭");
}
}
}
高频面试问答(深度解析)
1. Spring IoC容器的实现原理?
标准答案:
- BeanDefinition:Bean定义信息,存储Bean的元数据(类名、作用域、依赖等)
- BeanFactory:Bean工厂接口,负责Bean的创建和管理
- ApplicationContext:应用上下文,继承BeanFactory,提供更多企业级功能
- 依赖注入:通过反射机制实现,支持构造函数、setter、字段注入
深入追问与回答思路:
Q1: BeanFactory vs ApplicationContext?
java
// BeanFactory:延迟加载,按需创建Bean
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
// 此时Bean还未创建
UserService userService = factory.getBean("userService"); // 此时才创建
// ApplicationContext:容器启动时创建所有单例Bean
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 容器启动时已经创建了所有单例Bean,启动稍慢但运行时更快
UserService userService = context.getBean("userService"); // 直接获取
对比总结:
- BeanFactory:轻量级,延迟加载,适合资源受限环境
- ApplicationContext:功能丰富,启动时加载,适合大多数应用(推荐)
Q2: 循环依赖如何解决?
Spring通过三级缓存机制解决单例Bean的循环依赖问题:
java
// 三级缓存定义(DefaultSingletonBeanRegistry)
// 1. singletonObjects:一级缓存,存放完整的Bean(已初始化完成)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 2. earlySingletonObjects:二级缓存,存放早期Bean(未完成属性注入)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 3. singletonFactories:三级缓存,存放Bean工厂(ObjectFactory)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 解决流程(A依赖B,B依赖A):
// 步骤1:创建A,调用getSingleton("A")
// - 一级缓存:无
// - 二级缓存:无
// - 三级缓存:无
// - 创建A实例(未完成属性注入),放入三级缓存
// 步骤2:A需要注入B,调用getBean("B")
// - 创建B实例(未完成属性注入),放入三级缓存
// 步骤3:B需要注入A,调用getBean("A")
// - 一级缓存:无
// - 二级缓存:无
// - 三级缓存:有A的ObjectFactory
// - 从三级缓存获取A的早期对象,放入二级缓存,返回给B
// 步骤4:B注入A完成,B初始化完成
// - B从三级缓存移除,放入一级缓存
// 步骤5:A注入B完成,A初始化完成
// - A从二级缓存移除,放入一级缓存
循环依赖的限制:
- ✅ 支持:单例Bean的构造函数注入循环依赖(通过三级缓存)
- ❌ 不支持:原型Bean的循环依赖(每次都创建新实例)
- ❌ 不支持:构造函数注入的循环依赖(无法解决,会抛出异常)
Q3: 依赖注入的方式有哪些?优先级如何?
java
// 方式1:构造函数注入(推荐,Spring官方推荐)
@Service
public class UserService {
private final UserRepository userRepository;
// 优点:强制依赖、不可变、便于测试
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 方式2:setter注入
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 方式3:字段注入(不推荐,但常见)
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
}
// 方式4:方法注入(特殊场景)
@Service
public class UserService {
@Autowired
public void injectDependencies(UserRepository userRepository,
OrderRepository orderRepository) {
// 可以注入多个依赖
}
}
注入优先级(当有多种注入方式时):
- 构造函数注入
- setter注入
- 字段注入
Q4: @Autowired、@Resource、@Inject的区别?
| 注解 | 来源 | 默认注入方式 | 是否支持required | 是否支持name |
|---|---|---|---|---|
| @Autowired | Spring | byType,找不到则byName | ✅ | ❌ |
| @Resource | JSR-250 | byName,找不到则byType | ❌ | ✅ |
| @Inject | JSR-330 | byType | ✅ | ✅(需配合@Named) |
java
// @Autowired:Spring专用,按类型注入
@Autowired
private UserRepository userRepository; // 按UserRepository类型查找
@Autowired(required = false) // 允许为null
private Optional<UserRepository> userRepository;
// @Resource:JSR-250标准,按名称注入
@Resource(name = "userRepositoryImpl") // 按名称查找
private UserRepository userRepository;
// @Inject:JSR-330标准,功能类似@Autowired
@Inject
private UserRepository userRepository;
Q5: 如何自定义Bean的作用域?
java
// 1. 实现Scope接口
public class ThreadScope implements Scope {
private final ThreadLocal<Map<String, Object>> threadLocal =
ThreadLocal.withInitial(HashMap::new);
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = threadLocal.get();
return scope.computeIfAbsent(name, k -> objectFactory.getObject());
}
@Override
public Object remove(String name) {
return threadLocal.get().remove(name);
}
// ... 其他方法
}
// 2. 注册自定义作用域
@Configuration
public class AppConfig {
@Bean
public static CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("thread", new ThreadScope());
return configurer;
}
}
// 3. 使用自定义作用域
@Component
@Scope("thread")
public class ThreadLocalBean {
// 每个线程一个实例
}
延伸阅读
- 《Spring源码深度解析》--- 郝佳
- Spring官方文档:https://spring.io/docs
- Spring源码:https://github.com/spring-projects/spring-framework