【小家Spring】BeanFactory体系和ApplicationContext体系,两大体系各接口分析、区别和联系(上)

简介: 【小家Spring】BeanFactory体系和ApplicationContext体系,两大体系各接口分析、区别和联系(上)

前言

前面已经讲述了很多Spring容器启动、解析、依赖注入等等源码层面的东西了,但是小伙伴有没有一种感觉就是:Spring的设计者把面向对象使用到了极致(使用得非常的好),并且它吧职责单一原则也是使用到了极致。


它各个功能区块,通过接口都进行隔离得很开,这是让Spring能组件化开发,可插拔,变得如此优秀、普适的重要原因。


它的IOC和AOP主要围绕两大阵营展开的,也就是咱们今天的主题:BeanFactory体系和ApplicationContext体系,里面接口众多、抽象实现也众多,因此到了这个节点,我们是时候去宏观的看看这两大阵营的内容了。


Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)

Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)

Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)


BeanFactory和ApplicationContext 区别


基本区别


BeanFactory:BeanFacotry是Spring中最原始的Factory,里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。它没有AOP功能、Web应用功能等等

ApplicationContext:应用上下文,继承BeanFactory接口(因而提供BeanFactory所有的功能),ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承。它是Spring的一各更高级的容器,提供了更多的有用的功能:


  1. 国际化(MessageSource)(ApplicationContext.getMessage()拿到国际化消息)
  2. 访问资源,如URL和文件(ResourceLoader) (ApplicationContext acxt =new ClassPathXmlApplicationContext("/applicationContext.xml");能直接读取文件内容)
  3. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
  4. 消息发送、响应机制(ApplicationEventPublisher)
  5. AOP(拦截器)


相同点:BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册


两者装载bean的区别


BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;(它只去加载Bean的定义信息,显示调用getBean()才会真正去实例化),这样懒加载的优点是:启动快,启动时占用资源少,但具有第一次惩罚的特点


ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化(所有的单例、非懒加载的Bean都会容器启动时候立马实例化);

立马加载好的优点有(缺点你懂的,这里不提了):

1、启动时都初始化完成了,所以运行时会更快。

2、我们就能在系统启动的时候,尽早的发现系统中的配置问题 (因为启动时就得实例化处理)

3、可以(建议)把费时的操作放到系统启动中完成(比如初始化本地缓存、获取连接池的链接等等操作)


BeanFactory体系


先贴一张BeanFactory的继承图表,直观展示该家族的成员们

image.png


现在把这些接口分层级,进行逐一说明。


这个里给结论:只要 context 上下文未关闭,ContextRefreshedEvent事件可以多次触发刷新动作, 某些ApplicationContext支持”热”刷新。

比如,XmlWebApplicationContext 支持热刷新, GenericApplicationContext就不支持。

简单的说AbstractRefreshableApplicationContext都支持热刷新(所以它命名为Refreshable嘛),而GenericApplicationContext以及它的子类都是不能支持热舒心的


一级接口:BeanFactory(IOC的始祖)


只有一个,那就是BeanFactory,它提供如下基本方法:

public interface BeanFactory {
  String FACTORY_BEAN_PREFIX = "&";
  Object getBean(String name) throws BeansException;
  <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
  Object getBean(String name, Object... args) throws BeansException;
  <T> T getBean(Class<T> requiredType) throws BeansException;
  <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
  //@since 4.1
  // 包含Bean定义或者包含单例Bean实例,都会返回true(不管这个Bean是具体的还是抽象的、lazy的还是eager的,in scope or not 都会返回true)
  boolean containsBean(String name);
  // 若是单例,getBean()每次都会返回同一个实例
  boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
  boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
  //@since 4.2
  // 名称为name的Bean的类型,是否和给定的类型匹配(4.2新增的重载接口,传的是ResolvableType ,更加强大了)
  boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
  boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
  // name对应的Bean的类型
  Class<?> getType(String name) throws NoSuchBeanDefinitionException;
  // name对应的Bean的别名们(通过别名也可以依赖注入哦~~~)  通过alia别名,也可以getBean()
  String[] getAliases(String name);
}


这10来个方法,很明显,这是一个典型的工厂模式的工厂接口

需要注意的是,Spring5.1之后在此接口上又加入了如下两个接口:根据类型获取一个provider


// 返回指定bean的提供程序(Provider),允许延迟(这是重点)按需检索实例,包括可用性和唯一性选项
// @since 5.1
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
// @since 5.1
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);


二级接口:HierarchicalBeanFactory、ListableBeanFactory、AutowireCapableBeanFactory


3个子接口,进行了一定功能上的增强


HierarchicalBeanFactory:分层的Bean工厂


提供父容器的访问功能.至于父容器的设置,需要找三级接口ConfigurableBeanFactory的setParentBeanFactory(接口把设置跟获取给拆开了,这是为何呢?Spring早期设计的Bug?).

public interface HierarchicalBeanFactory extends BeanFactory {
  //返回本Bean工厂的父工厂(至于父工厂怎么设置进去的,却放在了三级接口(个人感觉是Spring的Bug哈哈))
  //这个方法实现了工厂的分层
  @Nullable
  BeanFactory getParentBeanFactory();
  //本地工厂是否包含这个Bean(忽略其他所有父工厂)。这也是分层思想的体现。
  boolean containsLocalBean(String name);
}

ListableBeanFactory:可将Bean逐一列出的工厂


它提供了提供容器中bean迭代的功能,不再需要一个个bean地查找.比如可以一次获取全部的bean(太暴力了),根据类型获取bean等等。它的这个功能在Spirng内部有大量的应用。


选哟注意的是:如果同时实现了HierarchicalBeanFactory,返回值不会考虑父类BeanFactory,只考虑当前factory定义的类.当然也可以使用BeanFactoryUtils辅助类来查找祖先工厂中的类。除了getBeanNamesOfType和getBeansOfType这两个方法,其余方法的逻辑都不会考虑父容器的Bean,只会考虑本容器自己的Bean


注意:getBeanDefinitionCount和containsBeanDefinition的实现方法因为效率比较低,还是频繁使用为好.


public interface ListableBeanFactory extends BeanFactory {
  // 这三个都是和Bean定义信息有关的方法
  boolean containsBeanDefinition(String beanName);
  int getBeanDefinitionCount();
  String[] getBeanDefinitionNames();
  //返回匹配给定类型(包括子类)的所有bean的名字,如果是普通bean,则是bean定义的名字,如果是 FactoryBean,则是其getObjectType方法返回的对象的名字
  // 这个方法只考虑最顶层的bean(top-level beans),内部嵌套的bean(nested beans)即便可能匹配指定的类型也不考虑(不支持内部类)
  // 在许多实现中,此方法返回的结果与调用getBeansOfType(type, true, true)一样
  // 这个方法返回的bean名称应该尽可能与后台配置的bean定义顺序一样
  // 此方法只会
  // 若没有符合条件的,返回的空数组,而不是null
  @since 4.2
  String[] getBeanNamesForType(ResolvableType type);
  // 同上
  String[] getBeanNamesForType(@Nullable Class<?> type);
  // includeNonSingletons:false表示只查单例Bean。true表示包含prototype或者其它Scope的Bean们
  // allowEagerInit:主要是解决FactoryBean的情况。若为false,只会去检查FactoryBean本身,若为true,FactoryBean本身和它的产生的对象都会被检查匹配
  // 上面的方法底层调用的为:return getBeanNamesForType(type, true, true); 所以上面方法是全拿(一般不建议调用)
  String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
  //返回匹配给定类型(包含子类)的实例,可能是通过bean定义创建,也可以是FactoryBean时其getObjectType返回(注意:此处返回的是实例了,不再是Bean定义了)
  //此方法仅考虑最顶层bean,不含其内部嵌套的bean,即使内部嵌套的bean匹配给定类型
  //此方法返回的结果与调用getBeansOfType(type, true, true)一样
  // 实现者:Map的顺序要尽最大可能的与配置时一样
  // 备注:此方法但凡一调用,即使有些Bean只是Bean定义的话,也会被立马初始化的~~~~~
  <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
  <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;
  // 这个接口会把所有标注有指定注解的Bean的定义信息的BeanName返回
  //@since 4.0
  String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
  // 返回的实例~~~~
  Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
  //查找指定bean的指定类型的注解。
  // 注意:如果本类没找到,还会去它的接口、它的父类里面找,这个厉害了我的哥
  // getBeanNamesForAnnotation依赖于此接口
  @Nullable
  <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) throws NoSuchBeanDefinitionException;
}



正如这个工厂接口的名字所示,这个工厂接口最大的特点就是可以列出工厂可以生产的所有实例。

这个工厂作为二级接口,有多个个独有的方法,扩展了跟BeanDefinition的功能,提供了BeanDefinition、BeanName、注解有关的各种操作

AutowireCapableBeanFactory:自动装配的Bean工厂


(该接口特别重要,扩展出来的方法也非常的多)对于想要拥有自动装配能力,并且想把这种能力暴露给外部应用的BeanFactory类需要实现此接口。


正常情况下,不要使用此接口,应该更倾向于使用BeanFactory或者ListableBeanFactory接口。此接口主要是针对框架之外,没有向Spring托管Bean的应用。通过暴露此功能,Spring框架之外的程序,具有自动装配等Spring的功能


需要注意的是,ApplicationContext接口并没有实现此接口,因为应用代码很少用到此功能,如果确实需要的话,可以调用ApplicationContext的getAutowireCapableBeanFactory方法,来获取此接口的实例。


另外,如果一个类实现了此接口,那么很大程度上它还需要实现BeanFactoryAware接口。它可以在应用上下文中返回BeanFactory

public interface AutowireCapableBeanFactory extends BeanFactory {
  // 用于标识外部自动装配功能是否可用。但是此标识不影响正常的(基于注解的等)自动装配功能的使用
  int AUTOWIRE_NO = 0; // 不注入
  int AUTOWIRE_BY_NAME = 1; // 根据名称注入
  int AUTOWIRE_BY_TYPE = 2; // 根据类型注入
  int AUTOWIRE_CONSTRUCTOR = 3; // 根据构造器注入
  @Deprecated
  int AUTOWIRE_AUTODETECT = 4; // 标识自动识别一种装配策略来实现自动装配(Spring3.0后废弃)
  //创建一个给定Class的实例。它会处理各种带有注解的域和方法,并且会调用所有Bean初始化时所需要调用的回调函数
  //它会执行所有的关于Bean生命周期的接口方法如BeanPostProcessor
  //此方法并不意味着by-name或者by-type方式的自动装配,如果需要使用这写功能,可以使用其下面的重载方法
  <T> T createBean(Class<T> beanClass) throws BeansException;
  // 和上面的区别为:创建这个Bean的时候可以指定autowireMode,然后可以把它需要注入的Bean都注入进来(这个Mode会放在Bean定义里,在依赖注入的时候会有用)
  // Bean定义的默认值为:autowireMode = AUTOWIRE_NO;显然是不会开启自动装配的
  // 在populateBean()给属性赋值(依赖注入的时候,会使用到此模式)
  Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
  //通过调用给定Bean的after-instantiation及post-processing接口,对bean进行装配值
  // 此方法主要是用于处理Bean中带有注解的字段和方法。
  // 此方法并不意味着by-name或者by-type方式的自动装配,如果需要使用这些功能,可以使用其重载方法autowireBeanProperties
  // 只会调用populateBean
  void autowireBean(Object existingBean) throws BeansException;
  //通过指定的自动装配策略来初始化一个Bean 注意:他会创建一个新的Bean
  //需要注意的是:此方法不会调用Bean上注册的诸如BeanPostProcessors的回调方法
  // 只会调用populateBean
  Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
  //通过指定的自动装配方式来对给定的已经存在的Bean进行自动装配
  //不过会调用指定Bean注册的BeanPostProcessors等回调函数来初始化Bean。
  //如果指定装配方式为AUTOWIRE_NO的话,不会自动装配属性,但是,但是,但是依然会调用BeanPiostProcesser等回调方法。
  //只会调用populateBean
  void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
  //配置参数中指定的bean,包括自动装配其域,对其应用如setBeanName功能的回调函数。并且会调用其所有注册的post processor.
  // beanName表示在Bean定义中的名称。
  // populateBean和initializeBean都会被调用
  Object configureBean(Object existingBean, String beanName) throws BeansException;
  //简单的说,就是把Bean定义信息里面的一些东西,赋值到已经存在的Bean里面
  // 除了InstantiationAwareBeanPostProcessor的回调方法外,此方法不会在Bean上应用其它的例如BeanPostProcessors
  void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
  // 实例化这个Bean,会调用所有的postProcessors 方法
  Object initializeBean(Object existingBean, String beanName) throws BeansException;
  //调用参数中指定Bean的postProcessBeforeInitialization/postProcessorsAfterInitialization方法  初始化之前、之后
  Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
  Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
  //销毁参数中指定的Bean,同时调用此Bean上的DisposableBean和DestructionAwareBeanPostProcessors方法
  //在销毁途中,任何的异常情况都只应该被直接捕获和记录,而不应该向外抛出。
  void destroyBean(Object existingBean);
  //查找唯一符合指定类的实例,如果有,则返回实例的名字和实例本身
  //底层依赖于:BeanFactory中的getBean(Class)方法
  <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;
  //解析出在Factory中与指定Bean有指定依赖关系的Bean(@Autowired依赖注入的核心方法)
  @Nullable
  Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;
  //descriptor 依赖描述 (field/method/constructor)
  //requestingBeanName 依赖描述所属的Bean
  //autowiredBeanNames 与指定Bean有依赖关系的Bean
  //typeConverter 用以转换数组和连表的转换器
  //备注:结果可能为null,毕竟容器中可能不存在这个依赖嘛~~~~~~~~~~~~~~~~
  @Nullable
  Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;
}


一共8个跟自动装配有关的方法,实在是繁杂,2个执行BeanPostProcessors的方法,2个分解指定依赖的方法(依赖注入)


从宏观上看,AutowireCapableBeanFactory提供了如下能力:


  1. 为已经实例化的对象装配属性,这些属性对象都是Spring管理的;
  2. 实例化一个类型,并自动装配,这些属性对象都是Spring管理的,实例化的类可以不被Spring管理(这点特别重要)。所以这个接口提供功能就是自动装配bean相关的,具体实现方式,其实我们在之前的getBean()解析时候已经有了,请参考:【小家Spring】AbstractBeanFactory#getBean()、doGetBean完成Bean的初始化、实例化,以及BeanPostProcessor后置处理器源码级详细分析


此接口主要是针对框架之外,没有向Spring托管Bean的应用。通过暴露此功能,Spring框架之外的程序,y也能具有自动装配的能力(此接口赋予它的)。


可以使用这个接口集成其它框架。捆绑并填充(注入)并不由Spring管理生命周期并已存在的实例.像集成WebWork的Actions 和Tapestry Page就很实用

一般应用开发者不会使用这个接口,所以像ApplicationContext这样的外观实现类不会实现这个接口


为了更深入的了解本接口的使用,请参照我专门写的一篇博文,脱离Spring容器来实现依赖注入(脱离容器):

【小家Spring】为脱离Spring IOC容器管理的Bean赋能【依赖注入】的能力,并分析原理(借助AutowireCapableBeanFactory赋能)



相关文章
|
27天前
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
55 6
|
1月前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
57 14
|
1月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
3月前
|
监控 Java 应用服务中间件
Spring和Spring Boot的区别
Spring和Spring Boot的主要区别,包括项目配置、开发模式、项目依赖、内嵌服务器和监控管理等方面,强调Spring Boot基于Spring框架,通过约定优于配置、自动配置和快速启动器等特性,简化了Spring应用的开发和部署过程。
83 19
|
3月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
734 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
3月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
119 2
|
3月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
276 2
|
3月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
235 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
4月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
200 5
|
Java 应用服务中间件 数据库连接
Spring全家桶之Spring篇深度分析(一)
Spring 框架不局限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力。
Spring全家桶之Spring篇深度分析(一)