深入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 启动之后实例化。


目录
相关文章
|
2月前
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
112 6
|
3月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
57 0
|
2月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
147 14
|
2月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
3月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
4月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
4月前
|
XML 前端开发 Java
拼多多1面:聊聊Spring MVC的工作原理!
本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖
71 0
|
Java Spring 容器
Spring中BeanFactory和FactoryBean的区别?
一位工作了4年的小伙伴,去京东面试被问到这样一个问题,Spring中的BeanFactory和FactoryBean有什么区别?因为没有看过源码,当时就感觉这是一个文字游戏,感觉没什么区别? 那今天,我就给大家来聊清楚。另外,往期面试题解析中配套的文档我已经准备好,想获得的可以在我的煮叶简介中找到。好了,我们先来看BeanFactory。
80 0
|
9月前
|
XML Java 数据格式
Spring5源码(8)-BeanFactory和FactoryBean的区别
Spring5源码(8)-BeanFactory和FactoryBean的区别
76 0
|
XML Java 数据格式
Spring中BeanFactory和FactoryBean详解
Spring中BeanFactory和FactoryBean详解
377 1