01.01 Spring核心|IoC容器深度解析

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的优势

  1. 解耦:对象不直接依赖具体实现,而是依赖接口
  2. 可测试:可以轻松注入mock对象进行单元测试
  3. 可维护:修改实现类不影响使用方
  4. 灵活性:运行时决定使用哪个实现

实战案例:订单服务

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提供了两个核心接口:BeanFactoryApplicationContext

复制代码
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 {
    // 每个线程一个实例
}

作用域选择建议

  1. 默认使用singleton:大多数Bean应该是无状态的单例
  2. 有状态对象用prototype:如果对象有可变状态且需要隔离
  3. Web相关用request/session:存储请求或会话级别的数据
  4. 注意线程安全: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) {
        // 可以注入多个依赖
    }
}

注入优先级(当有多种注入方式时):

  1. 构造函数注入
  2. setter注入
  3. 字段注入

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 {
    // 每个线程一个实例
}

延伸阅读

相关推荐
人道领域2 小时前
【零基础学java】(Map集合)
java·开发语言
@淡 定2 小时前
Seata AT模式详细实例:电商下单场景
java
杀死那个蝈坦2 小时前
JUC并发编程day1
java·开发语言
Java中文社群2 小时前
国内直连GPT、Claude和Gemini?N8N这次更新真的绝了!
人工智能·后端
飞Link2 小时前
【Java】Linux(CentOS7)下安装JDK8(Java)教程
java·linux·运维·服务器
秋4272 小时前
基于tomcat的动静分离
java·tomcat
巨人张2 小时前
C++零基础游戏----“大鱼吃小鱼”
java·c++·游戏
伯明翰java2 小时前
Java接口
java·开发语言
云和数据.ChenGuang2 小时前
Java装箱与拆箱(面试核心解析)
java·开发语言·面试