深入Spring原理-1.BeanFactory与ApplicationContext的区别

简介: 深入Spring原理-1.BeanFactory与ApplicationContext的区别

容器接口


以一个SpringBoot项目启动来举例

public class Application{
  public static void main(String[] args){
    SpringApplication.run(Application.class);
  }
}

当我们点击run进入到方法内部的时候

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

实际上是有返回值的,返回值类型是ConfigurableApplicationContext,而当打开其类图结构时会发现是这样的:


Ctrl + Alt + U(打开类图的方式)

可以看到ConfigurableApplicationContext 是一个子接口,ApplicationContext是一个父接口,而BeanFactory是ApplicationContext的父接口。


而打印ConfigurableApplicationContext 类型的返回值时,会发现,其beanFactory里面有一个singletionObjects

其实也就是说 在ConfigurableApplicationContext 加载完成,对应的beanFactory里面的singletionObjects就也加载完成了。这个就是ApplicationContext的一个很大的优势,实现了自动加载。


通过上图也可以看出,BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能,如MessageSource 处理国际化资源的能力、ResourcePatternResolver 通配符匹配资源的能力(磁盘路径 类路径找到的文件)、ApplicationEventPublisher(发布事件对象)、EnvironmentCapable(读取Spring中的环境信息,环境变量)

System.out.println(context.getMessage("hi", null, Locale.CHINA));
        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
        System.out.println("----------------------------------------------------------");
        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {
            System.out.println(resource);
        }
        System.out.println("----------------------------------------------------------");
        System.out.println(context.getEnvironment().getProperty("java_home"));
        System.out.println(context.getEnvironment().getProperty("server.port"));
        System.out.println("----------------------------------------------------------");
        context.getBean(Component1.class).register();
@Component
public class Component1 {
    private static final Logger log = LoggerFactory.getLogger(Component1.class);
    @Autowired
    private ApplicationEventPublisher context;
    public void register() {
        log.debug("用户注册");
        context.publishEvent(new UserRegisteredEvent(this));
    }
}
@Component
public class Component2 {
    private static final Logger log = LoggerFactory.getLogger(Component2.class);
    @EventListener
    public void aaa(UserRegisteredEvent event) {
        log.debug("{}", event);
        log.debug("发送短信");
    }
}


BeanFactory


当我们双击BeanFactory进入时,ctrl + f12发现:

发现表面上只有getBean,但实际上控制反转、基本的依赖注入,直至Bean的生命周期的各种功能,其实都由其实现类提供。

进入其实现类(ctrl + N)DefaultListableBeanFactory 发现 其impl 了 ConfigurableListableBeanFactory,而ConfigurableListableBeanFactory又impl了ListableBeanFactory。


比如说在代码里创建一个DefaultListableBeanFactory 对象 如下:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

注册一组配置进去

// bean 的定义(class, scope, 初始化, 销毁)
AbstractBeanDefinition beanDefinition =  BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
@Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }
 static class Bean1 {
        private static final Logger log = LoggerFactory.getLogger(Bean1.class);
        public Bean1() {
            log.debug("构造 Bean1()");
        }
        @Autowired
        private Bean2 bean2;
        public Bean2 getBean2() {
            return bean2;
        }
    }
    static class Bean2 {
        private static final Logger log = LoggerFactory.getLogger(Bean2.class);
        public Bean2() {
            log.debug("构造 Bean2()");
        }
    }

此时打印输出 beanFactory.getBeanDefinitionNames() 发现只有config被注册进去了,所以也就说明了@Configuration并没有解析,beanFactory缺少了解析 @bean等注解的能力,功能并不完整。


于是应该加入 一些常用的后置处理器(主要是配置注解相关的配置)、BeanFactoryPostProcessor、BeanPostProcessor:

AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
                .forEach(beanPostProcessor -> {
            System.out.println(">>>>" + beanPostProcessor);
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });

此时打印发现能够正常的将 bean1 bean2打印出来,也就代表着bean1 bean2被注册进去了。到了这里其实能够发现,现在BeanFactory 当我们把这些后置处理器都加上之后已经和 ApplicationContext很像了,但是还不够,在前面ApplicationContext 加载完成,beanFactory里面的singletionObjects就也加载完成了,而我们这里显然并不能做到预加载,还是需要 System.out.println(beanFactory.getBean(Bean1.class).getBean2()); 才能够进行懒加载,而ApplicationContext已经实现了预加载,那么就应该添加这个:

beanFactory.preInstantiateSingletons(); // 准备好所有单例

此时,就能够实现预加载,而此时,BeanFactory已经变得和ApplicationContext 基本一样了。


调用顺序


AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

这句前面提到了其实是添加一些 注解相关的配置,进入查看找到

def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);

其中分别是解析Autowired 和 Resource注解的

其实在代码中可以看到是有明显的先后顺序的,如果我们在stream中sort排序,那么实际上是比较的AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor里面的 getOrder方法

所以Autowired 和 Resource的顺序还是有讲究的。


ApplicationContext


基于ApplicationContext的扩展有很多,但是正如前面对比的那样,不仅不需要像BeanFactory那样 引入Bean后置处理器和BeanFactory后置处理器,而且还能预加载,每一个Bean都在ApplicationContext启动之后被初始化。


如下所示:

ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("a02.xml");
for (String name : context.getBeanDefinitionNames()) {
    System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
<!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
    <bean id="bean1" class="com.itheima.a02.A02.Bean1"/>
    <!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
    <bean id="bean2" class="com.itheima.a02.A02.Bean2">
        <!-- 依赖注入, 建立与 bean1 的依赖关系 -->
        <property name="bean1" ref="bean1"/>
    </bean>

或者不使用配置类,使用注解的方式:

AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);
for (String name : context.getBeanDefinitionNames()) {
    System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
@Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
        @Bean
        public Bean2 bean2(Bean1 bean1) {
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }


BeanFactory VS ApplicationContext


通过上面的分析,其实也就更能清晰的知道ApplicationContext强在哪里了:


  • 自动的 BeanPostProcessor 注册。
  • 自动的 BeanFactoryPostProcessor 注册。
  • 方便的 MessageSource、ResourcePatternResolver、ApplicationEventPublisher、EnvironmentCapable实现类使用。
  • ApplicationEvent 的发布与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化。


目录
相关文章
|
1月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
64 0
|
29天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
66 1
|
29天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
67 0
|
2天前
|
监控 Java 应用服务中间件
spring和springboot的区别
spring和springboot的区别
11 1
|
3天前
|
安全 Java API
Spring工厂API与原理
Spring工厂API与原理
27 10
|
11天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
【Spring系列】Sping VS Sping Boot区别与联系
|
15天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
21 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
30天前
|
XML 缓存 Java
天天用 Spring,bean 实例化原理你懂吗
天天用 Spring,bean 实例化原理你懂吗
17 0
|
8月前
|
Java Spring 容器
Spring中BeanFactory和FactoryBean的区别?
一位工作了4年的小伙伴,去京东面试被问到这样一个问题,Spring中的BeanFactory和FactoryBean有什么区别?因为没有看过源码,当时就感觉这是一个文字游戏,感觉没什么区别? 那今天,我就给大家来聊清楚。另外,往期面试题解析中配套的文档我已经准备好,想获得的可以在我的煮叶简介中找到。好了,我们先来看BeanFactory。
32 0
|
3月前
|
XML Java 数据格式
Spring5源码(8)-BeanFactory和FactoryBean的区别
Spring5源码(8)-BeanFactory和FactoryBean的区别
31 0