Spring源码-1.BeanFactory与ApplicationContext的区别

156 阅读4分钟
  • 👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家
  • 📕系列专栏:Spring源码、JUC源码
  • 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
  • 🍂博主正在努力完成2023计划中:源码溯源,一探究竟
  • 📝联系方式:nhs19990716,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬👀

容器接口

以一个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(打开类图的方式)

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

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

4.png

其实也就是说 在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("发送短信");
    }
}

9.png

BeanFactory

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

2.png

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

3.png

进入其实现类(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注解的

5.png

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

6.png

所以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>

7.png

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

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;
        }
    }

8.png

BeanFactory VS ApplicationContext

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

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