Spring源码解析 - BeanFactory系列接口解析(二)

目录

1、如图BeanFactory接口相关结构图

2、接下来具体分析下各个接口

(1)、BeanFactory

(2)、HierarchicalBeanFactory

(3)、ListableBeanFactory

(4)、AutowireCapableBeanFactory

(5)、ConfigurableBeanFactory

(6)、ApplicationContext

(7)、WebApplicationContext

(8)、ConfigurableApplicationContext

(9)、ThemeSource

(10)、MessageSource

(11)、ResourceLoader

(12)、ApplicationEventPublisher


这里我们通过这些接口的学习,可以大致了解BeanFactory体系各接口如何分工合作,以此为基础供以后逻辑与深入的学习。

1、如图BeanFactory接口相关结构图

2、接下来具体分析下各个接口

关于BeanFactory接口与部分接口已站在前人的肩膀上整理了,具体请看Spring源码解析 - BeanFactory接口源码解析与相关主接口解析(一)

已经过的这里只简单概述下:

(1)、BeanFactory

BeanFactory是Spring bean容器的根接口.

每个bean都是通过string类型bean name或ID进行标识.这边提供了设计模式单例,原型的替代实现.如果bean name配置为单例,应用内只会获取到一个实例.如果配置为原型,那么可以实例化好后填充属性(基于用户的配置).BeanFactory作为应用集中配置管理的地方,极大简便应用开发,这样开发人员可以集中与业务.

(2)、HierarchicalBeanFactory

提供父容器的访问功能.至于父容器的设置,需要找ConfigurableBeanFactory的setParentBeanFactory。

(3)、ListableBeanFactory

获取bean时,Spring 鼓励使用这个接口定义的api. 还有个Beanfactory方便使用.其他的4个接口都是不鼓励使用的.

提供容器中bean迭代的功能,不再需要一个个bean地查找.比如可以一次获取全部的bean(太暴力了),根据类型获取bean.在看SpringMVC时,扫描包路径下的具体实现策略就是使用的这种方式(那边使用的是BeanFactoryUtils封装的api).

 如果同时实现了HierarchicalBeanFactory,返回值不会考虑父类BeanFactory,只考虑当前factory定义的类.当然也可以使用BeanFactoryUtils辅助类来查找祖先工厂中的类. 

 这个接口中的方法只会考虑本factory定义的bean.这些方法会忽略ConfigurableBeanFactory的registerSingleton注册的单例bean(getBeanNamesOfType和getBeansOfType是例外,一样会考虑手动注册的单例).当然BeanFactory的getBean一样可以透明访问这些特殊bean.当然在典型情况下,所有的bean都是由external bean定义,所以应用不需要顾虑这些差别.

(4)、AutowireCapableBeanFactory

在BeanFactory基础上实现对已存在实例的管理.

可以使用这个接口集成其它框架,捆绑并填充并不由Spring管理生命周期并已存在的实例.像集成WebWork的Actions 和Tapestry Page就很实用.一般应用开发者不会使用这个接口,所以像ApplicationContext这样的外观实现类不会实现这个接口,如果真手痒痒可以通过ApplicationContext的getAutowireCapableBeanFactory接口获取. 

(5)、ConfigurableBeanFactory

定义BeanFactory的配置.

这边定义了太多太多的api,比如类加载器,类型转化,属性编辑器,BeanPostProcessor,作用域,bean定义,处理bean依赖关系,合并其他ConfigurableBeanFactory,bean如何销毁.

定义了两个作用域: 单例和原型.可以通过registerScope来添加.SCOPE_SINGLETON,SCOPE_PROTOTYPE

(6)、ApplicationContext

用于为应用程序提供配置的中央接口。这在应用程序运行时是只读的,但如果实现支持,则可以重新加载。

ApplicationContext接口继承众多接口,集众多接口功能与一身,为Spring的运行提供基本的功能支撑。根据程序设计的“单一职责原则”,其实每个较顶层接口都是“单一职责的”,只提供某一方面的功能,而ApplicationContext接口继承了众多接口,相当于拥有了众多接口的功能,下面看看它的主要功能:

  • 首先,它是个BeanFactory,可以管理、装配bean,可以有父级BeanFactory实现Bean的层级管理(具体到这里来说它可以有父级的ApplicationContext,因为ApplicationContext本身就是一个BeanFactory。这在web项目中很有用,可以使每个Servlet具有其独立的context, 所有Servlet共享一个父级的context),它还是Listable的,可以枚举出所管理的bean对象。
  • 其次,它是一个ResourceLoader,可以加载资源文件;
  • 再次,它可以管理一些Message实现国际化等功能;
  • 还有,它可以发布事件给注册的Listener,实现监听机制。

因此,作为Spring提供的较之 BeanFactory 更为先进的IoC容器实现, ApplicationContext 除了拥有BeanFactory 支持的所有功能之外,还进一步扩展了基本容器的功能,包括 BeanFactoryPostProcessor 、 BeanPostProcessor 以及其他特殊类型bean的自动识别、容器启动后bean实例的自动初始化、国际化的信息支持、容器内事件发布等

内部方法如下:

@Nullable
String getId();返回此应用程序上下文的唯一ID。

String getApplicationName();返回此上下文所属的已部署应用程序的名称。

String getDisplayName();返回此上下文的友好名称。

long getStartupDate();首次加载此上下文时返回时间戳。

返回父上下文,如果没有父上下文,则返回{@code null}
这是上下文层次结构的根。
@return父上下文,或{@code null}如果没有父上下文
@Nullable
ApplicationContext getParent();
通过getAutowireCapableBeanFactory这个方法将AutowireCapableBeanFactory这个接口暴露给外部使用,AutowireCapableBeanFactory这个接口一般在applicationContext的内部是较少使用的,它的功能主要是为了装配applicationContext管理之外的Bean。
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

(7)、WebApplicationContext

用于为Web应用程序提供配置的界面。 这在应用程序运行时是只读的,但如果实现支持,则可以重新加载。

 

WebApplicationContext是一个接口,并没有提供比较多的操作,主要有一个函数是getServletContext,用来获取Servlet上下文,因为spring 的IO容器在web中是保存在ServletContext中的。

因此专门为web应用准备的,他允许从相对于web根目录的路劲中装载配置文件完成初始化工作,从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便web应用可以访问spring上下文,spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext src)方法来获得WebApplicationContext对象。

内部方法如下:

      /**
       * 用于在成功启动时将根WebApplicationContext绑定到的Context属性。
       * 注意:如果根上下文的启动失败,则此属性可以包含异常或错误作为值。 使用 
       * WebApplicationContextUtils可以方便地查找根WebApplicationContext。
       */
	String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

	/**
	 * 请求范围的范围标识符:“request”。除了标准范围“singleton”和“prototype”之外还支持
	 */
	String SCOPE_REQUEST = "request";

	/**
	 * 会话范围的范围标识符:“会话”。 除了标准范围“singleton”和“prototype”之外,还支持。
	 */
	String SCOPE_SESSION = "session";

	/**
	 * Scope identifier for the gl全局Web应用程序范围的范围标识符:“application”。 除了标准范围“singleton”和“prototype”之外,还支持。
	 */
	String SCOPE_APPLICATION = "application";

	/**
	 * 工厂中ServletContext环境bean的名称
	 * @see javax.servlet.ServletContext
	 */
	String SERVLET_CONTEXT_BEAN_NAME = "servletContext";

	/**
	 * 工厂中ServletContext init-params环境bean的名称。
       * <p>注意:可能与ServletConfig参数合并。
       * ServletConfig参数覆盖同名的ServletContext参数。
	 */
	String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";

	/**
	 * 工厂中ServletContext属性环境bean的名称。
	 */
	String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";


	/**
	 * 返回此应用程序的标准Servlet API ServletContext。
	 */
	@Nullable
	ServletContext getServletContext();

(8)、ConfigurableApplicationContext

此接口结合了所有ApplicationContext需要实现的接口。因此大多数的ApplicationContext都要实现此接口。它在ApplicationContext的基础上增加了一系列配置应用上下文的功能。配置应用上下文和控制应用上下文生命周期的方法在此接口中被封装起来,以免客户端程序直接使用。

内部方法:

    /**
     * 应用上下文配置时,这些符号用于分割多个配置路径
     */
    String CONFIG_LOCATION_DELIMITERS = ",; \t\n";

    /**
     * BeanFactory中,ConversionService类所对应的bean的名字。如果没有此类的实例的话
     * 则使用默认的转换规则
     */
    String CONVERSION_SERVICE_BEAN_NAME = "conversionService";

    /**
     * LoadTimeWaver类所对应的Bean在容器中的名字。如果提供了该实例,上下文会使用临时的ClassLoader
     * 这样,LoadTimeWaver就可以使用bean确切的类型了
     */
    String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";

    /**
     * Environment类在容器中实例的名字
     */
    String ENVIRONMENT_BEAN_NAME = "environment";

    /**
     * System系统变量在容器中对应的Bean的名字
     */
    String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";

    /**
     * System 环境变量在容器中对应的Bean的名字
     */
    String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";


    /**
     * 设置容器的唯一ID
     */
    void setId(String id);

    /**
     * 设置此容器的父容器
     * 需要注意的是,父容器一经设定就不应该修改。并且一般不会在构造方法中对其进行配置,因为很多时候。
     * 其父容器还不可用。比如WebApplicationContext。
     */
    void setParent(ApplicationContext parent);

    /**
     * 设置容器的Environment变量
     */
    void setEnvironment(ConfigurableEnvironment environment);

    /**
     * 以COnfigurableEnvironment的形式返回此容器的环境变量。以使用户更好的进行配置
     */
    @Override
    ConfigurableEnvironment getEnvironment();

    /**
     * 此方法一般在读取应用上下文配置的时候调用,用以向此容器中增加BeanFactoryPostProcessor。
     * 增加的Processor会在容器refresh的时候使用。
     */
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);

    /**
     * 向容器增加一个ApplicationListener,增加的Listener用于发布上下文事件如refresh和shutdown等
     * 需要注意的是,如果此上下文还没有启动,那么在此注册的Listener将会在上下文refresh的时候,全部被调用
     * 如果上下文已经是active状态的了,就会在multicaster中使用
     */
    void addApplicationListener(ApplicationListener<?> listener);

    /**
     * 向容器中注入给定的Protocol resolver,允许多个实例同时存在。
     * 在此注册的每一个resolver都将会在上下的标准解析规则之前使用。因此,某种程度上来说
     * 这里注册的resolver可以覆盖上下文的resolver
     */
    void addProtocolResolver(ProtocolResolver resolver);

    /**
     * 加载资源配置文件(XML、properties,Whatever)。
     * 由于此方法是一个初始化方法,因此如果调用此方法失败的情况下,要将其已经创建的Bean销毁。
     * 换句话说,调用此方法以后,要么所有的Bean都实例化好了,要么就一个都没有实例化
     */
    void refresh() throws BeansException, IllegalStateException;

    /**
     * 向JVM注册一个回调函数,用以在JVM关闭时,销毁此应用上下文。
     */
    void registerShutdownHook();

    /**
     * 关闭此应用上下文,释放其所占有的所有资源和锁。并销毁其所有创建好的singleton Beans
     * 实现的时候,此方法不应该调用其父上下文的close方法,因为其父上下文具有自己独立的生命周期
     * 多次调用此方法,除了第一次,后面的调用应该被忽略。
     */
    @Override
    void close();

    /**
     * 检测此FactoryBean是否被启动过。
     */
    boolean isActive();

    /**
     * 返回此应用上下文的容器。
     * 千万不要使用此方法来对BeanFactory生成的Bean做后置处理,因为单例Bean在此之前已经生成。
     * 这种情况下应该使用BeanFactoryPostProcessor来在Bean生成之前对其进行处理。
     * 通常情况下,内容容器只有在上下文是激活的情况下才能使用。因此,在使用此方法前,可以调用
     * isActive来判断容器是如可用
     */
    ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

(9)、ThemeSource

ThemeSource的作用是根据主题名找到具体的主题。 

由可以解析的对象实现的接口。这使得给定“主题”的消息的参数化和国际化成为可能。

内部方法如下:

@Nullable
Theme getTheme(String themeName);返回给定主题名称的Theme实例。返回的主题将解析特定于主题的消息,代码,文件路径等(例如Web环境中的CSS和图像文件)

(10)、MessageSource

此接口是一个用于消息解析的策略接口,其支持消息的参数化和国际化。 
Spring提供两种现成的实现类:构建于ResourceBundle基础之上的ResourceBundleMessageSource和ReloadableResourceBundleMessageSource类用于在不重启虚拟器的情况下重新加载消息。

内部方法如下:

/**
	 * 解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。
	 * @param 需要进行解析的code,对应资源文件中的一个属性名
	 * @param 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
	 * @param defaultMessage 当对应code对应的信息不存在时需要返回的默认值
	 * @param locale 对应的Locale
	 * @return
	 */
	String getMessage(String code, Object[] args, String defaultMessage, Locale locale);

	/**
	 * 解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常NoSuchMessageException
	 * @param code 需要进行解析的code,对应资源文件中的一个属性名
	 * @param args 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
	 * @param locale 对应的Locale
	 * @return 
	 * @throws NoSuchMessageException 如果对应的code不能被解析则抛出该异常
	 */
	String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;

	/**
	 * 通过传递的MessageSourceResolvable对应来解析对应的信息
	 * @param resolvable 
	 * @param locale 对应的Locale
	 * @return 
	 * @throws NoSuchMessageException 如不能解析则抛出该异常
	 */
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

具体请参考:https://blog.csdn.net/elim168/article/details/77891450

(11)、ResourceLoader

 Spring为了方便程序调用不同类型的资源而定义的接口。Spring提供若干实现,方便我们针对不同的资源获取资源文件以及对具体内容的操作。常用的有以下三种:

  1. 通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
  2. 通过 ClassPathResource 以类路径的方式进行访问;
  3. 通过 ServletContextResource 以相对于Web应用根目录的方式进行访问。

用于加载资源的策略接口(策略包括classpath或者file system等类型)。Spring中的的所有应用上下文都应该拥有此功能和其拓展功能-ResourcePatternResolver。 
DefaultResourceLoader是此接口的一个实现,通常在ApplicationContext以外的类中使用,如ResourceEditor

内部方法如下:

    /** 如果要加载根目录下的资源,则使用classpath开头 */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;


    /**
     * 返回一个能解析指定目录Resource的对象
     * 该对象必须是可重用的,例如必须支持Resource的getInputStream方法多次调用而保持不变
     * 必须支持全限定的URLs,例如"file:C:/test.dat"
     * 必须支持全限定的伪URLs,例如"classpath:test.dat"
     * 必须支持相对文件路径,如"WEB-INF/test.dat"
     * 需要注意的是,参数中指定了路径的Resource不一定存在,在使用资源之前
     * 必须先调用Resource的exists方法检查资源是否存在
     */
    Resource getResource(String location);

    /**
     * 返回此类使用的ClassLoader
     * 如果有需要使用ClassLoader的代码,可以直接调用此方法来得到实例,而不需要
     * 通过Thread上下文获取ClassLoader
     */
    ClassLoader getClassLoader();

(12)、ApplicationEventPublisher

ApplicationEventPublisher是ApplicationContext的父接口之一。这接口的作用是:该接口的功能是publish Event,向事件监听器(Listener)发送事件消息

功能就是发布事件,也就是把某个事件告诉的所有与这个事件相关的监听器

内部方法如下:

/**
	 *通知在应用程序事件的此应用程序中注册的所有<strong>匹配</ strong>侦听器。 事件可以是框架事件(例如RequestHandledEvent)或特定于应用程序的事件。
	 */
	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	/**
	 * 通知在此事件应用程序中注册的所有<strong>匹配</ strong>侦听器。<p>如果指定的{@code事件}不是{@link ApplicationEvent},则它将包装在{@link PayloadApplicationEvent}中。
	 */
	void publishEvent(Object event);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平凡之路无尽路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值