BeanFactory体系结构
BeanFactory
BeanFactory作为Spring容器的顶层接口,那么它的作用一定是最简单,最核心的,特性如下:
- 基础的容器
- 定义了作用域的概念
- 集成了环境配置
- 支持多种类型的配资源
- 层次性的设计(父子结构)
- 完整的生命周期控制机制
Javadoc
BeanFactory是Spring中管理Bean的容器,它是Spring容器的顶层接口,它的子类都是为了扩展实现某些额外的特性,如:层次性(HierarchicalBeanFactory),可搜索性(ListableBeanFactory),可配置性(ConfigurableBeanFactory)等;
默认情况下,BeanFactory中的Bean只有单实例 Bean(singleton)和原型 Bean(prototype),从Spring 2.0 开始,根据具体的应用程序上下文(如:Web 环境中的request和session 作用域 )可以使用更多作用域;其中,产生单实例Bean和原型Bean所用的API是相同的,都是用@Scope注解来声明,然后由BeanFactory创建;BeanFactory是所有程序组件的注册中心,所有的Bean最终都会在BeanFactory中创建和保存,另外,BeanFactory还集成了应用程序组件的配置;
一般来说更好是通过setter方法或构造器注入的方式使用依赖注入,而不是通过如BeanFactory进行依赖查找的方式;
通常情况下,BeanFactory会加载存储在配资源中的BeanDefinition,并使用org.springframework.beans包中的API来配置bean;Spring可以支持的配置源类型(LDAP,RDBMS,XML,Properties等)有多种;
BeanFactory可以支持父子结构,父子结构由HierarchicalBeanFactory实现;如果BeanFactory实例中没有找到指定的Bean,则会向父工厂搜索查找,BeanFactory实例中的Bean应该覆盖任何父工厂中的同名Bean;
BeanFactory接口实现了尽可能支持标准Bean的生命周期接口;
BeanFactory的子接口
HierarchicalBeanFactory
HierarchicalBeanFactory从类名可以体现它是层次性的BeanFactory,它是由BeanFactory实现的子接口,可以在ConfigurableBeanFactory接口中找到用于BeanFactory的相应setParentBeanFactory方法,该方法允许以可配置的方式设置父工厂;
Javadoc
在该接口中有两个方法,一个是getParentBeanFactory方法,它可以用于获取父BeanFactory对象;另一个是containLocalBean方法,它是用于检查当前本地的容器是否有指定的Bean,而不会往上找父BeanFactory;
如果当前的BeanFactory中有指定的Bean,父BeanFactory是有可能存在指定的相同类型的Bean,测试如下:
查看代码
public class BeanFactoryDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
parent.registerBean(BeanA.class);
parent.refresh();
child.registerBean(BeanA.class);
child.setParent(parent);
child.refresh();
System.out.println("child BeanA:" + child.getBean(BeanA.class));
System.out.println("parent BeanA:" + parent.getBean(BeanA.class));
}
static class BeanA {
}
}
ListableBeanFactory
从类名可以看出ListableBeanFactory是具有可列举的BeanFactory;Javadoc
ListableBeanFactory是BeanFactory的扩展实现,它可以列举出容器中所有的Bean实例,相当于迭代器的特性; 如果当前BeanFactory同时也是HierarchicalBeanFactory,则返回值会忽略BeanFactory的层次结构,仅仅与当前 BeanFactory中定义的Bean有关,除此之外,也可以使用BeanFactoryUtils来考虑父 BeanFactory中的Bean; BeanFactory可以具有层次性,因此在列举所有Bean的时候需要考虑是选择获取父容器在内的所有Bean,还是选择只获取当前容器的Bean,Spring最终选择只获取当前容器的Bean;如果需要获取所有Bean,可以借助BeanFactoryUtils工具类来实现(其中,该工具类中以IncludingAncestors结尾的方法代表可以返回包含父容器的); ListableBeanFactory中的方法仅遵循当前工厂的BeanDefinition,它们将忽略通过其他方式(如ConfigurableBeanFactory的registerSingleton方法)注册的任何实例Bean(但getBeanNamesForType和getBeansOfType除外),它们也会检测这种手动注册的单实例Bean; 手动注册Bean测试如下: 创建两个普通的类查看代码
public class BeanA {
}
查看代码
public class BeanB {
}
查看代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="beanA" class="org.example.bean.BeanA"/>
</beans>
查看代码
public class ListableBeanFactoryDemo {
public static void main(String[] args) {
ClassPathResource resource = new ClassPathResource("bean.xml");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions(resource);
// 打印容器中的所有Bean
System.out.print("加载xml文件后容器中的Bean:");
Stream.of(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
// 手动注册一个单实例Bean
beanFactory.registerSingleton("beanB", new BeanB());
// 再打印容器中的所有Bean
System.out.print("手动注册单实例Bean后容器中的所有Bean:");
Stream.of(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
}
}
查看代码
System.out.println("容器中的所有BeanB Name:" + Arrays.toString(beanFactory.getBeanNamesForType(BeanB.class)));
System.out.println("容器中的所有BeanB Type:" + beanFactory.getBeansOfType(BeanB.class));
AutowireCapableBeanFactory
从类名可以看出AutowireCapableBeanFactory是支持自动注入的BeanFactory;Javadoc
AutowireCapableBeanFactory本身可以支持自动注入的,而且还可以为现有的一些Bean自动注入,其中,现有的一些Bean是指不被Spring管理的Bean; AutowireCapableBeanFactory一般不需要使用,而是在与其他框架集成时使用;在跟其他框架集成时,如果其他框架的一些Bean实例无法让Spring控制,但又需要注入一些由Spring管理的对象,那就可以使用此接口; 该接口没有在ApplicationContext中实现,但是它可以通过ApplicationContext#getAutowireCapableBeanFactory方法进行获得; 通过BeanFactoryAware接口注入的BeanFactroy是AutowireCapableBeanFactory,它可以直接强转获得;ConfigurableBeanFactory
ConfigurableBeanFactory是具备可配置的BeanFactory;一个类的属性设置为private属性后,提供的getter方法表示该属性可读,提供的setter方法方法表示该属性是可写的,在Spring的BeanFactory也是这样的,普通的BeanFactory只有get相关的操作,而Configurable开头的BeanFactory或Application则具备了set相关的操作;
Javadoc
大多数BeanFactory的实现类都会实现这个可配置的接口,除了BeanFactory接口中的基本获取方法外,还提供了配置BeanFactory的功能;
ConfigurableBeanFactory提供了可配置的功能,可以调用它里面定义的方法对BeanFactory进行修改,扩展操作;
ConfigurationBeanFactory接口并不推荐开发者在应用程序编码中使用,而是推荐使用BeanFactory或ListableBeanFactory;ConfigurationBeanFactory仅用于允许在框架内部进行即插即用,并允许对BeanFactory中的配置方法的特殊访问;
程序在运行期间不应该对BeanFactory再进行频繁的变动,此时应该只有读的操作,而不应该出现写的操作;Spring不希望开发者用ConfiguationBeanFactory,取而代之的是使用BeanFactory;
BeanFactory的实现
AbstractBeanFactory
AbstractBeanFactory是BeanFactory最基本的抽象实现,作为一个抽象类,只具备了一部分功能,不是完整的实现;
Javadoc
它是BeanFactory接口最基础的抽象实现类,提供ConfigurableBeanFactory SPI的全部功能,并且它可以从配置源(XML,LDAP,RDBMS等)获取BeanDefinition;
SPI全称是Service Provider Interface,它是JDK内置的一种服务提供发现的机制,不过在Spring中它有自己的一套实现;
此类可以提供单例Bean的缓存(通过其父类DefaultSingletonBeanRegistry),单例/原型Bean 的决定,FactoryBean 处理,Bean的别名,用于合并子BeanDefinition的BeanDefinition并以及Bean销毁(DisposableBean接口,自定义destroy方法);此外,它可以通过实现HierarchicalBeanFactory接口来管理BeanFactory层次结构(在未知 bean 的情况下委托给父工厂);
子类要实现主要模板方法是getBeanDefinition和createBean,分别为给定的Bean名称检索BeanDefinition,根据给定的BeanDefinition创建Bean实例,这些操作的默认实现可以在在DefaultListableBeanFactory和AbstractAutowireCapableBeanFactory中找到;
Spring中大量使用模板方法模式来设计核心组件,父类提供逻辑规范,子类提供具体步骤的实现;
AbstractAutowireCapableBeanFactory
AbstractAutowireCapableBeanFactory是AutowireCapableBeanFactory接口的实现,它可以实现组件的自动注入;
Javadoc
AbstractAutowireCapableBeanFactory集成了AbstractBeanFactory抽象类,还额外实现了AutowireCapbleBeanFactory接口,那实现了这个接口表示可以实现自动注入;此外,AbstractAutowireCapableBeanFactory还实现了AbstractBeanFactory的createBean方法,createBean方法不是最终实现Bean创建的逻辑,只是说具备了创建Bean的功能;而另一个方法doCreateBean,它才是真正执行创建Bean的逻辑,它同样是在AbstractAutowireCapableBeanFactory中定义,并且它是protected方法,没有子类重写它;
提供Bean的创建(具有构造方法的解析),属性填充,属性注入(包括自动装配)和初始化;处理运行时 Bean 的引用,解析托管集合,调用初始化方法等;支持自动装配构造函数,按名称的属性和按类型的属性;AbstractAutowireCapableBeanFactory跟AbstractBeanFactory不太一样的,AbstractAutowireCapableBeanFactory是没有把全部模板方法都实现,它保留了resolveDependency方法,这个方法的作用是解析Bean的成员中定义的属性依赖关系;
AbstractAutowireCapableBeanFactory实现了对Bean的创建、赋值、注入、初始化的逻辑,但它不负责BeanDefinition加载到BeanFactory;
DefaultListableBeanFactory
DefaultListableBeanFactory这个类是唯一一个目前使用的BeanFactory的实现;
Javadoc
DefaultListableBeanFactory是Spring的ConfigurableListableBeanFactory和BeanDefinitionRegistry接口的默认实现,它是基于BeanDefinition的BeanFactory实现,可以通过后置处理器进行扩展;
DefaultListableBeanFactory在AbstractAutowireCapableBeanFactory的基础上完成了BeanDefinition注册的操作,这个BeanDefinition注册是通过BeanDefinitionRegistry接口实现的;特定的BeanDefinition格式的解释器通常是单独实现的,而不是作为BeanFactory的子类实现;
BeanFactory作为一个统一管理Bean组件的容器,它的核心工作就是控制Bean在创建阶段的生命周期,而对于Bean如何注册,如何被创建,有哪些依赖要被注入,这些都与BeanFactory无关,而是有专门的组件来处理;ListableBeanFactory的替代实现StaticListableBeanFactory,它实现起来相对简单,功能比较单一;
它只能管理单例的Bean,没有BeanDefinition等概念;
XmlBeanFactory
在Spring 3.1后,XmlBeanFactory正式被标注为过时,替代的方案是使用DefaultListableBeanFactory + XmlBeanDefinitionReader,这种设计更符合组件的单一职责原则,另外从Spring 3.0后出现了注解驱动的IOC容器,XML驱动的方式不应该单独成为一种方案,而是使用一个通用的容器,通过组合它来实现,这样可以实现配置源载体分离;
ApplicationContext
ApplicationContext是Spring最核心的接口,它继承了ListableBeanFactory,HierarchicalBeanFactory,ResourcePatternResolver,MessageSource,ApplicationEventPublisher,Environment,因此间接继承了BeanFactory;
Javadoc
ApplicationContext是为应用程序配置的接口,在应用程序运行时,它是只读的,如果它的实现支持,它可以重新加载;
ApplicationContext提供:- 用于访问应用程序组件的Bean工厂方法,继承自ListableBeanFactory接口;
- 以通用方式加载文件资源的能力,继承自ResourceLoader接口;
- 能够将事件发布给注册的监听器,继承自ApplicationEventPublisher接口;
- 解析消息的能力,支持国际化,继承自MessageSource接口;
- 从父context继承,在子容器中的定义将始终优先;例如,整个Web应用程序都可以使用单个父context,而每个servlet 都有其自己的子context,该子context独立于任何其他servlet的context;
Application是支持层级结构的,文档中描述的是父子context,context不仅包含容器,还包含动态增强,资源加载,事件监听机制等扩展功能,而容器只负责管理Bean实例;
ApplicationContext除了标准的BeanFactory生命周期外,它还实现了检测并调用ApplicationContextAware,以及ResourceLoader,ApplicationEventPublisherAware和MessageSourceAware;
其中,ApplicationContext间接继承了ResourceLoader,ApplicationEventPublisher,MessageSource,而xxxAware接口的作用是将Spring的xxx组件依赖注入到实现了Aware接口的对象实例中,最终调用这些xxxAware接口注入的是ApplicationContext本身;
ApplicationContext的子接口
ConfigurableApplicationContext
ConfigurableApplicationContext给ApplicatinContext提供了可配置的功能,实现了ConfigurableApplicationContext该接口的实现类可以被客户端代码修改内部某些配置;
Javadoc
ConfigurableApplicationContext给ApplicationContext添加了用于配置的功能;ConfigurableApplicationContext中扩展了setParent 、setEnvironment 、addBeanFactoryPostProcessor 、addApplicationListener等方法,这些都是可以改变ApplicationContext本身的方法; 配置和与生命周期相关的方法都封装在ConfigurableApplicationContext,以避免暴露给ApplicationContext的调用者,但是它一般情况下不希望让开发者调用,而是只调用refresh和close方法;ApplicationContext的扩展接口
EnvironmentCapable
在Spring中,以Capable结尾的接口,通常可以通过这个接口的某个特定方法(通常是getxxx方法)拿到特定的组件;
Environment是Spring中抽象出来的类似于运行环境的独立抽象,它内部存放着应用程序运行的一些配置; EnvironmentCapable是具有获取并公开Environment引用的接口;所有Spring的ApplicationContext都具有EnvironmentCapable功能,并且该接口主要用于在接受BeanFactory实例的框架方法中执行instanceof检查,以便可以与Environment进行交互;org.springframework.context.ConfigurableApplicationContext#getEnvironment
ApplicationContext 扩展了EnvironmentCapable,因此公开了getEnvironment方法;但是,ConfigurableApplicationContext重新定义了getEnvironment,并缩小了使用范围,以返回 ConfigurableEnvironment ;Environment是只读的,直到从ConfigurableApplicationContext访问它为止,此时也可以对其进行配置;
MessageSource
Javadoc
用于解析消息的策略接口,并支持消息的参数化和国际化;SpringFramework 为生产提供了两种现有的实现:- ResourceBundleMessageSource:建立在标准 java.util.ResourceBundle 之上,共享其局限性;
- ReloadableResourceBundleMessageSource:高度可配置,尤其是在重新加载消息定义方面;
ApplicationEventPublisher
ApplicationEventPublisher它是事件发布器;
Javadoc
ApplicationEventPublisher是封装事件发布功能的接口,它作为ApplicationContext的父接口;
Spring内部支持很强大的事件监听机制,而ApplicationContext作为容器的最顶级,需要实现观察者模式中发布器;
ResoucePatternResolver
ResoucePatternResolver从类名可以解释为资源模式解析器,它是根据特定的路径去解析资源文件;
Javadoc
ResoucePatternResolver是ResourceLoader的扩展,可以检查传入的ResourceLoader是否实现了此扩展接口,ResouceLoader实现最基本的解析,ResoucePatternResolver可以支持Ant形式的带星号(*)的路径解析;
ResoucePatternResolver是一个独立的实现,可在ApplicationContext外部使用,ResourceArrayPropertyEditor使用它来填充Resource数组中Bean属性;
ResoucePatternResolver基于路径匹配的解析器,这种扩展实现的特点是会根据特殊的路径来返回多个匹配到的资源文件; ResoucePatternResolver支持的是 Ant 风格的匹配模式,这种模式可以有如下写法:- /WEB-INF/*.xml:匹配 /WEB-INF 目录下的任意 xml 文件
- /WEB-INF/**/beans-*.xml:匹配 /WEB-INF 下面任意层级目录的 beans- 开头的 xml 文件
- /**/*.xml:匹配任意 xml 文件
ApplicationContext的实现类
AbstractApplicationContext
AbstractApplicationContext定义了绝大部分的应用上下文的特性和功能;
Javadoc
ApplicationContext接口的抽象实现,不强制用于配置的存储类型;简单地实现通用上下文功能,使用模板方法模式,需要具体的子类来实现抽象方法;
与普通的BeanFactory相比,此类自动注册在上下文中定义为Bean的BeanFactoryPostProcessors,BeanPostProcessors和ApplicationListeners, 因此,ApplicationContext能够检测在其内部Bean工厂中定义的特殊Bean; ApplicationContext 实现了国际化的接口MessageSource、事件广播器的接口ApplicationEventMulticaster;作为容器,ApplicationContext 也会把自己看成一个 Bean,以支持不同类型的组件注入需要; 默认情况下,AbstractApplicationContext加载资源文件的策略是直接继承了DefaultResourceLoader的策略,从类路径下加载;但在Web项目中,可能策略就不一样了,它可以从ServletContext 中加载(扩展的子类ServletContextResourceLoader等);注:AbstractApplicationContext中定义了一个控制ApplicationContext生命周期的核心方法,AbstractApplicationContext#refresh;
GenericApplicationContext
GenericApplicationContext是一个普通类,非抽象类,它是ApplicationContext的通用实现,它里面具备了ApplicationContext基本的功能;
Javadoc
GenericApplicationContext是ApplicationContext的通用实现,该实现拥有一个内部DefaultListableBeanFactory实例,并且不采用特定的BeanDefinition格式;另外它实现 了BeanDefinitionRegistry接口,以便允许将任何BeanDefinitionReader应用于该容器中;注:GenericApplicationContext内部中维护了一个DefaultListableBeanFactory实例,ApplicationContext与BeanFactory之间存在的不是继承关系,而是组合关系;
通过BeanDefinitionRegistry接口注册各种Bean的定义,然后调用refresh方法以使用应用程序上下文语义来初始化这些Bean(处理ApplicationContextAware,自动检测 BeanFactoryPostProcessors等);大致意思:由于GenericApplicationContext中组合了一个DefaultListableBeanFactory,而这个BeanFactory是在GenericApplicationContext的构造方法中就已经初始化了,那么初始化好的 BeanFactory就不允许在运行期间被重复刷新了;
BeanFactory刷新逻辑如下:
org.springframework.context.support.GenericApplicationContext#refreshBeanFactory
注解驱动的IOC容器可以导入XML配置文件,不过如果大多数都是XML配置的话,官方建议还是直接用ClassPathXmlApplicationContext或FileSystemXmlApplicationContext; 注解驱动的方式在开发时很灵活,但如果需要修改配置时,可能需要重新编译配置类;XML驱动的方式在修改配置时直接修改即可,不需要做任何额外的操作,但能配置的内容实在是有些有限; 对于应该以可刷新方式读取特殊bean定义格式的自定义应用程序上下文实现,考虑从AbstractRefreshableApplicationContext基类派生;
AbstractRefreshableApplicationContext
AbstractRefreshableApplicationContext与GenericApplicationContext最大区别之一是AbstractRefreshableApplicationContext可以被重复刷新;
Javadoc
AbstractRefreshableApplicationContext是ApplicationContext接口实现的抽象父类,支持多次调用refresh方法,每次都创建一个新的内部BeanFactory实例;通常这样的上下文将由一组配置文件驱动,以从中加载BeanDefinition; 每次都会创建一个新的内部BeanFactory实例,BeanFactory默认的实现是DefaultListableBeanFactory,而整个ApplicationContext的初始化中没有创建; org.springframework.context.support.AbstractRefreshableApplicationContext#beanFactory org.springframework.context.support.AbstractRefreshableApplicationContext#createBeanFactory 可刷新的ApplicationContext里面存放的BeanDefinition是可以被覆盖加载的,由于AbstractApplicationContext已经实现了ConfigurableApplicationContext接口,容器本身可以重复刷新,那么每次刷新时应该重新加载BeanDefinition,以及初始化Bean实例; 以特定的BeanDefinition格式读取的该父类的具体独立子类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,它们均从AbstractXmlApplicationContext基类扩展;AnnotationConfigApplicationContext支持@Configuration注解的类作为BeanDefinition源;
AbstractXmlApplicationContext
AbstractXmlApplicationContext是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext的直接父类;
Javadoc
方便ApplicationContext父类从包含XmlBeanDefinitionReader解析的 BeanDefinition 的 XML 文档中提取配置; 子类只需要实现getConfigResources或getConfigLocations方法;此外,它们可能会覆盖getResourceByPath方法,用来以特定于环境的方式解析相对路径,或覆盖getResourcePatternResolver方法,用来扩展模式解析;AbstractXmlApplicationContext根据不同子类(ClassPathXmlApplicationContext,FileSystemXmlApplicationContext)的实现,通过重写getResourceByPath或getResourcePatternResolver方法,选择不同的配置文件方式加载;
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory) 创建XmlBeanDefinitionReader解析XML文件,之后将XmlBeanDefinitionReader对象作为入参传入到AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)加载BeanDefinition;
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)
如果使用的是ClassPathXmlApplicationContext,ClassPathXmlApplicationContext则重写getConfigResources方法;
如果使用的是FileSystemXmlApplicationContext,FileSystemXmlApplicationContext则重写getConfigLocations方法;
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext是从classpath下加载XML配置文件的ApplicationContext;
Javadoc
ClassPathXmlApplicationContext是独立的基于 XML 的 ApplicationContext,它从classpath中获取配置文件,将纯路径解释为包含包路径的classpath资源名称(例如 mypackage / myresource.txt );
ClassPathXmlApplicationContext可以通过重写getConfigLocations方法来调整配置文件默认读取的位置,加载配置文件的方式还可以使用Ant模式匹配;
如果有多个配置位置,则较新的BeanDefinition会覆盖较早加载的文件中的BeanDefinition,可以利用它来通过一个额外的XML文件有意覆盖某些BeanDefinition; 由于ClassPathXmlApplicationContext继承了AbstractXmlApplicationContext,而 AbstractXmlApplicationContext 实际上是内部组合了一个 XmlBeanDefinitionReader ,所以就可以有一种组合的使用方式; 使用GenericApplicationContext或者子类AnnotationConfigApplicationContext,配合XmlBeanDefinitionReader则可以做到注解驱动和XML配置通用;
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext继承了GenericApplicationContext,因此它只能刷新一次;