IoC容器深度解析(二):ApplicationContext深入剖析,7大区别你知道几个?

系列文章第2篇 | 共3篇
难度:⭐⭐⭐ | 适合人群:想深入理解Spring容器的开发者


📝 上期回顾

上一篇我们学习了:

  • ✅ IoC(控制反转)和DI(依赖注入)的概念
  • ✅ 手写了100行代码实现简易IoC容器
  • ✅ 理解了Spring的BeanFactory接口
  • ✅ 掌握了Bean的注册、创建、注入流程

上期思考题解答:

Q1: 如何改造支持Setter注入?
A:createBean()方法中,创建对象后,通过反射调用setter方法注入依赖。(本篇会演示)

Q2: 循环依赖会怎样?Spring如何解决?
A: 我们的容器会栈溢出,Spring通过"三级缓存"解决。(第三篇详解)

Q3: BeanFactory和ApplicationContext有什么区别?
A: 今天详细解答!


💥 开场:一次生产事故的教训

时间: 周五晚上10点
地点: 家里(刚准备睡觉)
事件: 生产告警

手机响起: 📱 "叮叮叮!"

告警信息: "订单服务启动缓慢,超时30秒!"

我: "啥情况?" 😰(赶紧打开电脑)


查看日志:

2024-01-20 22:05:00 INFO  - 开始启动Spring容器...
2024-01-20 22:05:00 INFO  - 加载配置文件...
2024-01-20 22:05:01 INFO  - 扫描Bean定义...
2024-01-20 22:05:02 INFO  - 创建UserService...
2024-01-20 22:05:03 INFO  - 创建OrderService...
2024-01-20 22:05:04 INFO  - 创建PaymentService...
...
2024-01-20 22:05:28 INFO  - 创建NotificationService...
2024-01-20 22:05:29 INFO  - 所有Bean创建完成
2024-01-20 22:05:30 INFO  - 容器启动完成!

我: "卧槽,启动30秒?上周还是3秒!" 😱


第二天,找架构师老李:

我: "李哥,容器启动越来越慢,有办法优化吗?"

老李: "你用的什么容器?"

我: "BeanFactory啊,上次你说的..."

老李: "等等,生产环境用BeanFactory?" 😨

我: "有问题吗?"

老李: "问题大了!BeanFactory是懒加载,每次getBean都要创建,启动快但运行慢。生产环境应该用ApplicationContext,启动时就把所有Bean创建好,运行时直接用!"

我: "原来如此!那ApplicationContext和BeanFactory有什么区别?" 🤔

老李: "区别大了,至少7个!来,我给你画个图..."


🤔 第一问:ApplicationContext是什么?

Q1:先看继承关系

类图:

        BeanFactory (爷爷)
              ↑
              |
     ApplicationContext (父亲)
              ↑
              |
    ┌─────────┼─────────┐
    |         |         |
ClassPath  FileSystem  Web
  ↑          ↑          ↑
  |          |          |
XML/      XML/      XML/
Annotation Annotation Annotation

发现: ApplicationContext继承了BeanFactory

结论: ApplicationContext 一个BeanFactory,但功能更强!


Q2:ApplicationContext接口定义

位置: org.springframework.context.ApplicationContext

源码(简化版):

public interface ApplicationContext extends 
        EnvironmentCapable,           // 环境感知
        ListableBeanFactory,          // 可列举的BeanFactory
        HierarchicalBeanFactory,      // 层次化的BeanFactory
        MessageSource,                // 国际化消息
        ApplicationEventPublisher,    // 事件发布
        ResourcePatternResolver {     // 资源加载
    
    /**
     * 获取应用启动时间
     */
    long getStartupDate();
    
    /**
     * 获取父容器
     */
    ApplicationContext getParent();
    
    /**
     * 获取AutowireCapableBeanFactory
     */
    AutowireCapableBeanFactory getAutowireCapableBeanFactory();
}

看出来了吗? ApplicationContext继承了5个接口!这就是它功能强大的原因!


Q3:常用的实现类

1. ClassPathXmlApplicationContext

通过classpath下的XML配置文件创建:

// beans.xml在src/main/resources下
ApplicationContext context = 
    new ClassPathXmlApplicationContext("beans.xml");

UserService service = context.getBean("userService", UserService.class);

2. FileSystemXmlApplicationContext

通过文件系统路径的XML配置文件创建:

// 绝对路径或相对路径
ApplicationContext context = 
    new FileSystemXmlApplicationContext("/path/to/beans.xml");

UserService service = context.getBean("userService", UserService.class);

3. AnnotationConfigApplicationContext(最常用)

通过Java配置类创建:

@Configuration
@ComponentScan("com.example")
public class AppConfig {
    
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

// 使用
ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

UserService service = context.getBean(UserService.class);

4. WebApplicationContext(Web环境)

Spring MVC中的容器:

// 在web.xml中配置
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

// 在代码中获取
WebApplicationContext context = 
    WebApplicationContextUtils.getWebApplicationContext(servletContext);

🎯 第二问:BeanFactory vs ApplicationContext 七大区别

区别1:Bean的加载时机(最重要!)

BeanFactory:懒加载(Lazy Loading)

// 创建BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition("userService", 
    new RootBeanDefinition(UserService.class));

System.out.println("容器创建完成");  // ← 这时Bean还没创建

// 第一次调用getBean才创建
UserService service = factory.getBean("userService", UserService.class);
System.out.println("Bean创建完成");  // ← 这时才创建

输出:

容器创建完成          ← 快速启动
Bean创建完成          ← 延迟创建

ApplicationContext:饿加载(Eager Loading)

// 创建ApplicationContext
ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

System.out.println("容器创建完成");  // ← 这时所有Bean都创建好了

// 直接获取,不需要创建
UserService service = context.getBean(UserService.class);
System.out.println("Bean已经存在");

输出:

创建UserService...     ← 容器启动时就创建
创建OrderService...
创建PaymentService...
容器创建完成          ← 启动稍慢,但Bean都准备好了
Bean已经存在          ← 获取很快

对比表格:

维度BeanFactoryApplicationContext
加载时机懒加载(第一次getBean)饿加载(容器启动时)
启动速度快 ⚡稍慢 🐢
运行速度慢(需要创建) 🐢快(直接用) ⚡
内存占用小(按需创建)大(全部创建)
错误发现运行时才发现 ❌启动时就发现 ✅
适用场景资源受限、嵌入式生产环境、Web应用

结论:

  • 开发/测试: BeanFactory启动快,适合快速迭代
  • 生产环境: ApplicationContext更稳定,启动时就能发现错误

区别2:国际化支持

BeanFactory:不支持

// BeanFactory没有国际化接口
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// factory.getMessage(...);  ❌ 编译错误,没这个方法

ApplicationContext:支持国际化

准备国际化文件:

# messages_zh_CN.properties
user.welcome=欢迎,{0}!
user.goodbye=再见,{0}!

# messages_en_US.properties
user.welcome=Welcome, {0}!
user.goodbye=Goodbye, {0}!

配置类:

@Configuration
public class I18nConfig {
    
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBasename("messages");
        source.setDefaultEncoding("UTF-8");
        return source;
    }
}

使用:

ApplicationContext context = 
    new AnnotationConfigApplicationContext(I18nConfig.class);

// 中文
String msg = context.getMessage("user.welcome", 
    new Object[]{"张三"}, Locale.CHINA);
System.out.println(msg);  // 输出:欢迎,张三!

// 英文
msg = context.getMessage("user.welcome", 
    new Object[]{"Tom"}, Locale.US);
System.out.println(msg);  // 输出:Welcome, Tom!

应用场景: 多语言应用、国际化网站


区别3:事件发布机制

BeanFactory:不支持

// BeanFactory没有事件发布接口
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// factory.publishEvent(...);  ❌ 编译错误

ApplicationContext:支持事件机制

自定义事件:

/**
 * 用户注册事件
 */
public class UserRegisterEvent extends ApplicationEvent {
    
    private String username;
    private String email;
    
    public UserRegisterEvent(Object source, String username, String email) {
        super(source);
        this.username = username;
        this.email = email;
    }
    
    public String getUsername() {
        return username;
    }
    
    public String getEmail() {
        return email;
    }
}

事件监听器:

/**
 * 发送欢迎邮件监听器
 */
@Component
public class WelcomeEmailListener implements ApplicationListener<UserRegisterEvent> {
    
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        System.out.println("发送欢迎邮件给:" + event.getEmail());
    }
}

/**
 * 积分奖励监听器
 */
@Component
public class PointsRewardListener implements ApplicationListener<UserRegisterEvent> {
    
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        System.out.println("赠送新人积分给:" + event.getUsername());
    }
}

发布事件:

@Service
public class UserService {
    
    @Autowired
    private ApplicationContext context;
    
    public void register(String username, String email) {
        // 1. 保存用户
        System.out.println("保存用户:" + username);
        
        // 2. 发布注册事件
        UserRegisterEvent event = new UserRegisterEvent(this, username, email);
        context.publishEvent(event);
        
        System.out.println("用户注册完成");
    }
}

测试:

public class EventTest {
    public static void main(String[] args) {
        ApplicationContext context = 
            new AnnotationConfigApplicationContext(AppConfig.class);
        
        UserService userService = context.getBean(UserService.class);
        userService.register("张三", "zhangsan@example.com");
    }
}

输出:

保存用户:张三
发送欢迎邮件给:zhangsan@example.com
赠送新人积分给:张三
用户注册完成

优势:

  • ✅ 解耦:发布者和监听器互不依赖
  • ✅ 扩展性:新增监听器不影响现有代码
  • ✅ 异步:可以配置异步执行

区别4:资源加载能力

BeanFactory:需要手动加载

// 手动加载资源
Resource resource = new ClassPathResource("config.properties");
Properties props = new Properties();
props.load(resource.getInputStream());

ApplicationContext:强大的资源加载

加载classpath资源:

ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

// 单个资源
Resource resource = context.getResource("classpath:config.properties");
System.out.println("文件存在: " + resource.exists());

// 批量加载(支持通配符)
Resource[] resources = context.getResources("classpath*:mapper/*.xml");
System.out.println("找到 " + resources.length + " 个Mapper文件");

加载文件系统资源:

Resource resource = context.getResource("file:/path/to/file.txt");

加载URL资源:

Resource resource = context.getResource("https://example.com/data.json");

加载所有匹配的资源:

// 扫描所有jar包中的resources目录下的sql文件
Resource[] resources = context.getResources("classpath*:sql/*.sql");
for (Resource r : resources) {
    System.out.println(r.getFilename());
}

区别5:环境抽象(Environment)

BeanFactory:不支持

// BeanFactory没有环境抽象
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// factory.getEnvironment();  ❌ 没这个方法

ApplicationContext:完整的环境支持

获取配置属性:

ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

// 获取Environment
Environment env = context.getEnvironment();

// 读取系统属性
String javaHome = env.getProperty("java.home");
System.out.println("Java Home: " + javaHome);

// 读取环境变量
String path = env.getProperty("PATH");
System.out.println("PATH: " + path);

// 读取application.properties
String appName = env.getProperty("app.name");
System.out.println("App Name: " + appName);

// 检查Profile
boolean isDev = env.acceptsProfiles(Profiles.of("dev"));
System.out.println("开发环境: " + isDev);

Profile功能(环境切换):

@Configuration
public class DataSourceConfig {
    
    @Bean
    @Profile("dev")  // 开发环境
    public DataSource devDataSource() {
        return new HikariDataSource(...);  // H2内存数据库
    }
    
    @Bean
    @Profile("prod")  // 生产环境
    public DataSource prodDataSource() {
        return new HikariDataSource(...);  // MySQL数据库
    }
}

// 激活Profile
System.setProperty("spring.profiles.active", "dev");
ApplicationContext context = 
    new AnnotationConfigApplicationContext(DataSourceConfig.class);

区别6:BeanPostProcessor自动注册

BeanFactory:需要手动注册

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

// 注册Bean
factory.registerBeanDefinition("userService", 
    new RootBeanDefinition(UserService.class));

// 必须手动注册BeanPostProcessor
MyBeanPostProcessor processor = new MyBeanPostProcessor();
factory.addBeanPostProcessor(processor);  // ← 手动添加

UserService service = factory.getBean("userService", UserService.class);

ApplicationContext:自动注册

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("处理Bean:" + beanName);
        return bean;
    }
}

// 使用
ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

// BeanPostProcessor自动生效,不需要手动注册!

区别7:BeanFactory处理器(BeanFactoryPostProcessor)

BeanFactory:不支持

// BeanFactory不会处理BeanFactoryPostProcessor
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 即使注册了BeanFactoryPostProcessor,也不会自动执行

ApplicationContext:自动处理

自定义BeanFactoryPostProcessor:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        System.out.println("=== BeanFactory后置处理器执行 ===");
        
        // 可以修改Bean定义
        String[] beanNames = beanFactory.getBeanDefinitionNames();
        System.out.println("容器中有 " + beanNames.length + " 个Bean定义");
        
        // 修改某个Bean的定义
        if (beanFactory.containsBeanDefinition("userService")) {
            BeanDefinition bd = beanFactory.getBeanDefinition("userService");
            bd.setScope("prototype");  // 改成原型模式
            System.out.println("已将userService改为原型模式");
        }
    }
}

使用:

ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

// BeanFactoryPostProcessor自动执行
// 输出:
// === BeanFactory后置处理器执行 ===
// 容器中有 10 个Bean定义
// 已将userService改为原型模式

📊 七大区别总结表格

区别BeanFactoryApplicationContext重要性
1. 加载时机懒加载饿加载⭐⭐⭐⭐⭐
2. 国际化❌ 不支持✅ 支持⭐⭐⭐
3. 事件机制❌ 不支持✅ 支持⭐⭐⭐⭐
4. 资源加载基础强大⭐⭐⭐
5. 环境抽象❌ 不支持✅ 支持⭐⭐⭐⭐
6. BeanPostProcessor手动注册自动注册⭐⭐⭐⭐
7. BeanFactoryPostProcessor❌ 不支持✅ 支持⭐⭐⭐⭐

🔍 第三问:ApplicationContext启动流程源码分析

核心方法:refresh()

位置: org.springframework.context.support.AbstractApplicationContext

源码(简化版):

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备刷新上下文
        prepareRefresh();
        
        // 2. 获取BeanFactory(核心!)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // 3. 准备BeanFactory
        prepareBeanFactory(beanFactory);
        
        try {
            // 4. BeanFactory后置处理
            postProcessBeanFactory(beanFactory);
            
            // 5. 执行BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // 6. 注册BeanPostProcessor
            registerBeanPostProcessors(beanFactory);
            
            // 7. 初始化国际化资源
            initMessageSource();
            
            // 8. 初始化事件广播器
            initApplicationEventMulticaster();
            
            // 9. 刷新(模板方法,子类可扩展)
            onRefresh();
            
            // 10. 注册事件监听器
            registerListeners();
            
            // 11. 实例化所有单例Bean(核心!)
            finishBeanFactoryInitialization(beanFactory);
            
            // 12. 完成刷新
            finishRefresh();
        }
        catch (BeansException ex) {
            // 销毁已创建的Bean
            destroyBeans();
            
            // 取消刷新
            cancelRefresh(ex);
            
            throw ex;
        }
        finally {
            // 清理缓存
            resetCommonCaches();
        }
    }
}

流程图

ApplicationContext启动流程
│
├─ 1. prepareRefresh()
│    └─ 设置启动时间、激活标志
│
├─ 2. obtainFreshBeanFactory()  ← 核心
│    ├─ 创建BeanFactory
│    ├─ 加载Bean定义
│    └─ 返回BeanFactory
│
├─ 3. prepareBeanFactory()
│    ├─ 设置类加载器
│    ├─ 添加BeanPostProcessor
│    └─ 注册默认环境Bean
│
├─ 4-5. 执行BeanFactoryPostProcessor
│    └─ 修改Bean定义
│
├─ 6. 注册BeanPostProcessor
│    └─ 注册所有BeanPostProcessor
│
├─ 7-8-10. 初始化国际化、事件机制
│
├─ 11. finishBeanFactoryInitialization()  ← 核心
│    ├─ 实例化所有单例Bean
│    ├─ 依赖注入
│    └─ 初始化回调
│
└─ 12. finishRefresh()
     ├─ 发布ContextRefreshedEvent
     └─ 启动完成

关键步骤详解

步骤2:obtainFreshBeanFactory()

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 刷新BeanFactory(子类实现)
    refreshBeanFactory();
    
    // 返回BeanFactory
    return getBeanFactory();
}

// AnnotationConfigApplicationContext的实现
@Override
protected final void refreshBeanFactory() {
    // 创建DefaultListableBeanFactory
    DefaultListableBeanFactory beanFactory = createBeanFactory();
    
    // 设置序列化ID
    beanFactory.setSerializationId(getId());
    
    // 定制BeanFactory
    customizeBeanFactory(beanFactory);
    
    // 加载Bean定义(扫描@Component等)
    loadBeanDefinitions(beanFactory);
    
    this.beanFactory = beanFactory;
}

关键: ApplicationContext内部使用的就是DefaultListableBeanFactory


步骤11:finishBeanFactoryInitialization()

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 初始化类型转换服务
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)) {
        beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }
    
    // 冻结配置
    beanFactory.freezeConfiguration();
    
    // 实例化所有剩余的单例Bean(核心!)
    beanFactory.preInstantiateSingletons();
}

preInstantiateSingletons()源码:

@Override
public void preInstantiateSingletons() throws BeansException {
    // 获取所有Bean名称
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
    
    // 遍历所有Bean
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        
        // 不是抽象、是单例、不是懒加载
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            
            if (isFactoryBean(beanName)) {
                // FactoryBean特殊处理
                // ...
            }
            else {
                // 普通Bean:调用getBean创建
                getBean(beanName);  // ← 这里触发Bean创建!
            }
        }
    }
    
    // 触发所有单例Bean的初始化后回调
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            SmartInitializingSingleton smartSingleton = 
                (SmartInitializingSingleton) singletonInstance;
            smartSingleton.afterSingletonsInstantiated();
        }
    }
}

看到了吗?

  • ApplicationContext启动时会遍历所有Bean定义
  • 调用getBean()创建所有单例Bean
  • 这就是"饿加载"的实现!

💻 第四问:实战对比演示

准备测试Bean

@Component
public class UserDao {
    public UserDao() {
        System.out.println(">>> UserDao被创建,时间:" + 
            System.currentTimeMillis());
    }
}

@Component
public class UserService {
    
    @Autowired
    private UserDao userDao;
    
    public UserService() {
        System.out.println(">>> UserService被创建,时间:" + 
            System.currentTimeMillis());
    }
    
    public String getUser(Long id) {
        return "User-" + id;
    }
}

@Component
public class OrderService {
    
    @Autowired
    private UserService userService;
    
    public OrderService() {
        System.out.println(">>> OrderService被创建,时间:" + 
            System.currentTimeMillis());
    }
    
    public String createOrder(Long userId) {
        return "Order for " + userService.getUser(userId);
    }
}

测试1:BeanFactory懒加载

public class BeanFactoryTest {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        
        System.out.println("===== BeanFactory测试 =====\n");
        
        // 1. 创建BeanFactory
        System.out.println("--- 创建BeanFactory ---");
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        
        // 2. 注册Bean定义
        factory.registerBeanDefinition("userDao", 
            new RootBeanDefinition(UserDao.class));
        factory.registerBeanDefinition("userService", 
            new RootBeanDefinition(UserService.class));
        factory.registerBeanDefinition("orderService", 
            new RootBeanDefinition(OrderService.class));
        
        long end1 = System.currentTimeMillis();
        System.out.println("容器创建完成,耗时:" + (end1 - start) + "ms\n");
        
        // 3. 第一次getBean
        System.out.println("--- 第一次获取OrderService ---");
        OrderService orderService = factory.getBean("orderService", OrderService.class);
        long end2 = System.currentTimeMillis();
        System.out.println("获取Bean耗时:" + (end2 - end1) + "ms\n");
        
        // 4. 使用Bean
        System.out.println("--- 使用Bean ---");
        String result = orderService.createOrder(123L);
        System.out.println("结果:" + result);
        
        System.out.println("\n总耗时:" + (System.currentTimeMillis() - start) + "ms");
    }
}

输出:

===== BeanFactory测试 =====

--- 创建BeanFactory ---
容器创建完成,耗时:2ms

--- 第一次获取OrderService ---
>>> OrderService被创建,时间:1705737001234
>>> UserService被创建,时间:1705737001235
>>> UserDao被创建,时间:1705737001236
获取Bean耗时:15ms

--- 使用Bean ---
结果:Order for User-123

总耗时:18ms

分析:

  • ✅ 容器启动快(2ms)
  • ❌ 第一次使用慢(15ms创建Bean)
  • Bean在第一次getBean时才创建

测试2:ApplicationContext饿加载

public class ApplicationContextTest {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        
        System.out.println("===== ApplicationContext测试 =====\n");
        
        // 1. 创建ApplicationContext
        System.out.println("--- 创建ApplicationContext ---");
        ApplicationContext context = 
            new AnnotationConfigApplicationContext(AppConfig.class);
        
        long end1 = System.currentTimeMillis();
        System.out.println("容器创建完成,耗时:" + (end1 - start) + "ms\n");
        
        // 2. 获取Bean(已经创建好了)
        System.out.println("--- 获取OrderService ---");
        OrderService orderService = context.getBean(OrderService.class);
        long end2 = System.currentTimeMillis();
        System.out.println("获取Bean耗时:" + (end2 - end1) + "ms\n");
        
        // 3. 使用Bean
        System.out.println("--- 使用Bean ---");
        String result = orderService.createOrder(123L);
        System.out.println("结果:" + result);
        
        System.out.println("\n总耗时:" + (System.currentTimeMillis() - start) + "ms");
    }
}

输出:

===== ApplicationContext测试 =====

--- 创建ApplicationContext ---
>>> UserDao被创建,时间:1705737101234
>>> UserService被创建,时间:1705737101235
>>> OrderService被创建,时间:1705737101236
容器创建完成,耗时:156ms

--- 获取OrderService ---
获取Bean耗时:0ms

--- 使用Bean ---
结果:Order for User-123

总耗时:158ms

分析:

  • ❌ 容器启动慢(156ms,包含创建Bean)
  • ✅ 获取Bean快(0ms,直接从缓存取)
  • Bean在容器启动时就创建好了

对比总结

维度BeanFactoryApplicationContext
容器启动2ms ⚡156ms 🐢
首次获取Bean15ms 🐢0ms ⚡
Bean创建时机第一次getBean容器启动时
错误发现运行时启动时 ✅
总体性能启动快,运行慢启动慢,运行快 ✅

结论:

  • 开发环境: 两者都可以,看个人喜好
  • 生产环境: 强烈推荐ApplicationContext!

🎯 第五问:如何选择容器?

决策树

你的应用场景是?
│
├─ 资源极度受限(嵌入式设备)
│  └─ 选择:BeanFactory
│
├─ 快速原型开发、单元测试
│  └─ 选择:BeanFactory 或 ApplicationContext
│
├─ Web应用
│  └─ 选择:ApplicationContext(强制)
│
├─ 需要国际化
│  └─ 选择:ApplicationContext
│
├─ 需要事件机制
│  └─ 选择:ApplicationContext
│
├─ 生产环境
│  └─ 选择:ApplicationContext(强烈推荐)
│
└─ 不确定
   └─ 选择:ApplicationContext(默认选择)

官方建议

Spring官方文档说明:

You should use an ApplicationContext unless you have a good reason for not doing so.

翻译:除非你有充分的理由,否则应该使用ApplicationContext。

什么是"充分的理由"?

  1. 运行在资源极度受限的环境(如嵌入式设备)
  2. 应用只需要最基础的IoC功能
  3. 对启动时间有极致要求(毫秒级)

99%的情况应该用ApplicationContext!


💡 知识点总结

本篇你学到了什么?

ApplicationContext的定位

  • 继承BeanFactory,功能更强
  • 继承5个接口,提供额外功能

七大核心区别

  1. 加载时机:懒加载 vs 饿加载 ⭐⭐⭐⭐⭐
  2. 国际化:不支持 vs 支持
  3. 事件机制:不支持 vs 支持
  4. 资源加载:基础 vs 强大
  5. 环境抽象:不支持 vs 支持
  6. BeanPostProcessor:手动 vs 自动
  7. BeanFactoryPostProcessor:不支持 vs 支持

ApplicationContext启动流程

  • 12个关键步骤
  • refresh()方法是核心
  • preInstantiateSingletons()实现饿加载

实战对比

  • BeanFactory:启动快(2ms),运行慢
  • ApplicationContext:启动慢(156ms),运行快

选择建议

  • 99%场景用ApplicationContext
  • 生产环境必须用ApplicationContext

🤔 思考题

问题1: ApplicationContext启动时,所有Bean都会创建吗?有没有例外?

提示: 想想@Lazy注解、prototype作用域

问题2: 如果Bean创建过程中抛异常,ApplicationContext会怎样?BeanFactory呢?

提示: 启动时失败 vs 运行时失败

问题3: Bean的完整生命周期有哪些阶段?BeanPostProcessor在哪个阶段执行?

提示: 下一篇详细讲解11个步骤!


📢 下期预告(系列完结篇)

《IoC容器深度解析(三):Bean生命周期完整流程,11步骤深度剖析!》

下一篇我们将:

  • 深入Bean生命周期的11个完整步骤
  • 详解所有BeanPostProcessor的执行时机
  • 源码级分析AbstractAutowireCapableBeanFactory
  • 手写代码验证每个生命周期回调
  • 实战:自定义BeanPostProcessor
  • 解析Spring如何解决循环依赖(三级缓存)

系列完结篇,干货满满! 🚀


💬 互动时间

你在生产环境中遇到过容器启动慢的问题吗?
对ApplicationContext的7大区别,哪个最让你印象深刻?
还有哪些关于容器的疑问?

欢迎评论区讨论!💭


觉得有帮助?三连支持: 👍 点赞 | ⭐ 收藏 | 🔄 转发

看完这篇,BeanFactory和ApplicationContext不再混淆!


下一篇见(完结篇)! 👋