程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

spring源码分析 容器的功能拓展

发布于2021-06-12 13:02     阅读(311)     评论(0)     点赞(20)     收藏(2)


目录

简介

设置配置路径

扩展功能

环境准备

加载BeanFactory

定制Bean Factory

加载BeanDefinition

功能扩展

增加SpEL语言的支持

增加属性注册编辑器

使用自定义属性编辑器

注册Spring自带的属性编辑器Custom Date Editor

添加ApplicationContextAwareProcess可处理器

设置忽略依赖

注册依赖

BeanFactory的后处理

激活注册的Bean FactoryPostProcessor

BeanFactoryPostProcessor的典型应用

使用自定义Bean FactoryPostProcessor

激活BeanFactoryPostProcessor

注册BeanPostProcessor

初始化消息资源

初始化ApplicationEventMuliticaster

注册监听器

初始化非延迟加载单例

ConversionService的设置

冻结配置

初始化非延迟加载

finishRefresh

initLifecycleProcessor

onRefresh

publishEvent


注意:本文摘自  spring源码深度解析

简介

经过前面几章的分析,相信大家已经对Spring中的容器功能有了简单的了解,在前面的章 节中我们一直以BeanFactory接口以及它的默认实现类XmlBeanFactory为例进行分析,但是, Spring中还提供了另一个接口ApplicationContext, 用于扩展BeanFactory中现有的功能。

ApplicationContext和BeanFactory两者都是用于加载Bean的,但是相比之下,Application­Context提供了更多的扩展功能,简单一点说: ApplicationContext包含BeanFactory的所有功能。 通常建议比BeanFactory优先,除非在一些限制的场合,比如字节长度对内存有很大的影响时 (Applet)。绝大多数”典型的“企业应用和系统,ApplicationContext就是你需要使用的。

那么究竟ApplicationContext比BeanFactory多出了哪些功能呢?还需要我们进一步的探索。首先我们来看看使用两个不同的类去加载配置文件在写法上的不同。

使用BeanFactory方式加载XML。

BeanFactory  bf= new XmlBeanFactory(new ClassPathResource("BeanFactoryTest.Xml"));

使用ApplicationContext方式加载XML。

ApplicationContext bf= new ClassPathXmlApplicationContext("BeanFactoryTest.Xml");

同样,我们还是以ClassPathXmlApplicationContext作为切入点,开始对整体功能进行分析。

设置路径是必不可少的步骤,ClassPathXmlApplicationContext中可以将配置文件路径以数 组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析并进行加载。而对于解析 及功能实现都在refresh()中实现。

设置配置路径

在ClassPathXmlApplicationContext中支持多个配置文件以数组方式同时传入:

此函数主要用于解析给定的路径数组,当然,如果数组中包含特殊符号,如${var}, 那么 在resolvePath中会搜寻匹配的系统变量并替换。

扩展功能

设置了路径之后,便可以根据路径做配置文件的解析以及各种功能的实现了。可以说 refresh函数中包含了几乎ApplicationContext中提供的全部功能,而且此函数中逻辑非常清晰 明了,使我们很容易分析对应的层次及逻辑。

下面概括一下ClassPathXmlApplicationContext初始化的步骤,并从中解释一下它为我们 提供的功能。

1 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证。

在某种情况下项目的使用需要读取某些系统变量,而这个变量的设置很可能会影响着系统 的正确性,那么ClassPathXmlApplicationContext为我们提供的这个准备函数就显得非常必要, 它可以在Spring启动的时候提前对必需的变量进行存在性验证。

2. 初始化BeanFactory, 并进行XML文件读取。

之前有提到ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特征,那么 在这一步骤中将会复用BeanFactory中的配置文件读取解析及其他功能,这一步之后, ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行bean的提取等基础操作了。

3 对BeanFactory进行各种功能填充。

@Qualifier与@Autowired应该是大家非常熟悉的注解,那么这两个注解正是在这一步骤中 增加的支持。

4. 子类覆盖方法做额外的处理。

Spring之所以强大,为世人所推崇,除了它功能上为大家提供了便例外,还有一方面是它的完美架构,开放式的架构让使用它的程序员很容易根据业务需要扩展已经存在的功能。这种开放式的设计在Spring中随处可见,例如在本例中就提供了一个空的函数实现postProcess­BeanFactory来方便程序员在业务上做进一步扩展。

5 激活各种BeanFactory处理器。

6 注册拦截bean创建的bean处理器,这里只是注册,真正的调用是在getBean时候。

7 为上下文初始化Message源,即对不同语言的消息体进行国际化处理。

8 初始化应用消息广播器,并放入"applicationEventMulticaster" bean中。

9. 留给子类来初始化其他的bean。

10 在所有注册的bean中查找listener bean, 注册到消息广播器中。

11 初始化剩下的单实例(非惰性的)。

12 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出Context­RefreshEvent通知别人。

环境准备

prepareRefresh函数主要是做些准备工作,例如对系统属性及环境变盘的初始化及验证。

网上有入说其实这个函数没什么用,因为最后两句代码才是最为关键的,但是却没有什么逻辑

处理,initPropertySources是空的,没有任何逻辑,而getEnvironment().validateRequiredProperties 也因为没有需要验证的属性而没有做任何处理。其实这都是因为没有彻底理解才会这么说,这 个函数如果用好了作用还是挺大的。那么,该怎么用呢?我们先探索下各个函数的作用。

1. initPropertySources正符合Spring的开放式结构设计,给用户最大扩展Spring的能力。

用户可以根据自身的需要重写initPropertySources方法,并在方法中进行个性化的属性处理及 设置。

2. validateRequiredProperties则是对属性进行验证,那么如何验证呢?我们举个融合两句代 码的小例子来帮助大家理解。

假如现在有这样一个需求,工程在运行过程中用到的某个设置(例如VAR)是从系统环境 变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程可能不会工作。这 一要求可能会有各种各样的解决办法,当然,在Spring中可以这样做,你可以直接修改Spring 的源码,例如修改ClassPathXmlApplicationContext。当然,最好的办法还是对源码进行扩展, 我们可以自定义类:

我们自定义了继承自ClassPathXmlApplicationContext的MyClassPathXmlApplicationContext, 并重写了initPropertySources方法,在方法中添加了我们的个性化需求,那么在验证的时候也 就是程序走到getEnvironment(). validateRequiredProperties()代码的时候,如果系统并没有检测到对应VAR的环境变量,那么将抛出异常。当然我们还需要在使用的时候替换掉原有的 ClassPathXmlApplicationContext:

加载BeanFactory

obtainFreshBeanFactory方法从字面理解是获取BeanFactory。之前有说过,Application­Context是对BeanFactory的功能上的扩展,不但包含了BeanFactory的全部功能更在其基础上 添加了大量的扩展应用,那么o btainF reshBeanF actory正是实现BeanFactory的地方,也就是经 过了这个函数后ApplicationContext就已经拥有了BeanFactory的全部功能。

我们详细分析上面的每个步骤。

I. 创建DefaultListableBeanFactory。

在介绍BeanFactory的时候,不知道读者是否还有印象,声明方式为: BeanFactory bf= newXmlB eanFactory("beanFactoryTest.Xml") , 其中的XmlBeanFactory继承自DefaultListableBeanFactory, 并提供了XmlBeanDefinitionReader类型的reader属性,也就是说DefaultListableBeanFactory是容器的基础。必须首先要实例化,那么在这里就是实例化DefaultListableBeanFactory 的步骤。

2 指定序列化ID。

3 定制BeanFactory。

4 加载BeanDefinition。

5 使用全局变量记录BeanFactory类实例。

因为DefaultListableBeanFactory类型的变量BeanFactory是函数内的局部变量,所以要使用全局变量记录解析结果。

定制Bean Factory

这里已经开始了对BeanFactory的扩展,在基本容器的基础上,增加了是否允许覆盖是否允许扩展的设置并提供了注解@Qualifier和@Autowired的支持。

对于允许覆盖和允许依赖的设置这里只是判断了是否为空,如果不为空要进行设置,但是 并没有看到在哪里进行设置,究竟这个设置是在哪里进行设置的呢?还是那句话,使用子类覆盖方法,例如:

设置完后相信大家已经对于这两个属性的使用有所了解,或者可以回到前面的章节进行再 一次查看。对于定制BeanFactory, Spring还提供了另外一个重要的扩展,就是设置Autowire­CandidateResolver, 在bean加载部分中讲解创建Bean时,如果采用AutowireByType方式注入,那 么默认会使用Spring提供的SimpleAutowireCandidateResolver, 而对于默认的实现并没有过多的逻辑处理。在这里,Spring使用了QualifierA.nnotationAutowireCandidateResolver, 设置了这个解析 器后Spring就可以支持注解方式的注入了。

在讲解根据类型自定注入的时候,我们说过解析Autowire类型时首先会调用方法:

Object value= getAutowireCandidateResolver() .getSuggestedValue(descriptor);

因此我们知道,在QualifierAnnotationAutowireCandidateResolver中一定会提供了解析 Qualifier与Autowire注解的方法。

加载BeanDefinition

在第一步中提到了将ClassPathXmlApplicationContext与XmlBeanFactory创建的对比,在 实现配置文件的加载功能中除了我们在第一步中已经初始化的DefaultListableBeanFactory外, 还需要XmlBeanDefinitionReader来读取XML, 那么在这个步骤中首先要做的就是初始化 XmlBeanDefinitionReader。

使用XmlBeanDefinitionReader的loadBeanDefinitions方法进行配置文件的加载机注册相信大家已经不陌生,这完全就是开始BeanFactory的套路。因为在XmlBeanDefinitionReader中已 经将之前初始化的DefaultListableBeanFactory注册进去了,所以XmlBeanDefinitionReader所读 取的BeanDefinitionHolder都会注册到DefaultListableBeanFactory中,也就是经过此步骤,类型 DefaultListableBeanFactory的变显beanFactory已经包含了所有解析好的配置。

功能扩展

进入函数prepareBeanFactory前,Spring已经完成了对配置的解析,而ApplicationContext 在功能上的扩展也由此展开。

上面函数中主要进行了几个方面的扩展。

增加对SpEL语言的支持。

增加对属性编辑器的支持。

增加对一些内置类,比如EnvironmentAware、MessageSourceAware的信息注入。

设置了依赖功能可忽略的接口。

注册一些固定依赖的属性。

增加AspectJ的支持(会在第7章中进行详细的讲解)。

将相关环境变量及属性注册以单例模式注册。

增加SpEL语言的支持

Spring表达式语言全称为Spring Expression Language, 缩写为SpEL, 类似于Struts 2x中使 用的OGNL表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等,并 且能与Spring功能完美整合,比如能用来配置bean定义。SpEL是单独模块,只依赖于core 模块,不依赖于其他模块,可以单独使用。

SpEL使用#{...}作为定界符,所有在大框号中的字符都将被认为是SpEL, 使用格式如下:

当然,上面只是列举了其中最简单的使用方式,SpEL功能非常强大,使用好可以大大提高 开发效率,这里只为唤起读者的记忆来帮助我们理解源码,有兴趣的读者可以进一步深入研究。

在源码中通过代码BeanFactory.setBeanExpressionResolver(new StandardBeanExpression­Resolver())注册语言解析器,就可以对SpEL进行解析了,那么在注册解析器后Spring又是在 什么时候调用这个解析器进行解析呢?

之前我们讲解过Spring在bean进行初始化的时候会有属性填充的一步,而在这一步中 Spring会调用AbstractAutowireCapableBeanFactory类的a])])lyPro])ertyValues函数来完成功能。就在 这个函数中,会通过构造BeanDefinition ValueResolver类型实例valueResolver来进行属性值的解 析。同时,也是在这个步骤中一般通过AbstractBeanFactory中的evaluateBeanDefinitionString 方法去完成SPEL的解析。

当调用这个方法时会判断是否存在语言解析器,如果存在则调用语言解析器的方法进行解 析,解析的过程是在Spring的expression的包内,这里不做过多解释。我们通过查看对evaluate­BeanDefinitionString方法的调用层次可以看出,应用语言解析器的调用主要是在解析依赖注入 bean的时候,以及在完成bean的初始化和属性获取后进行属性填充的时候。

增加属性注册编辑器

在Spring DI注入的时候可以把普通属性注入进来,但是像Date类型就无法被识别。例如:

如果直接这样使用,程序则会报异常,类别转换不成功。因为在User Manager中的dataValue 属性是Date类型的,而在XML中配置的却是String类型的,所以当然会报异常。

Spring针对此问题提供了两种解决办法。

使用自定义属性编辑器

使用自定义属性编辑器,通过继承PropertyEditorSupport, 重写setAsText方法,具体步骤 如下。

在配置文件中引入类型为org.Springframework.beans.factory.config.CustomEditorConfigurer 的bean, 并在属性customEditors中加入自定义的属性编辑器,其中key为属性编辑器所对应 的类型。通过这样的配置,当Spring在注入bean的属性时一旦遇到了java.util.Date类型的属性会自动调用自定义的DateProperty Editor解析器进行解析,并用解析结果代替配置属性进行注入。

注册Spring自带的属性编辑器Custom Date Editor

通过注册Spring自带的属性编辑器Cust01nDateEditor, 具体步骤如下。

通过在配置文件中将自定义的DatePropertyEditorRegistrar注册进入org.Springframework.beans.factory.config.CustomEditorConfigurer的propertyEditorRegistrars属性中,可以具有与方法 1同样的效果。

我们了解了自定义属性编辑器的使用,但是,似乎这与本节中围绕的核心代码BeanFactory.add­PropertyEditorRegistrar·(new ResourceEditorRegistrar(this, getEnvironment())并无联系,因为在注册自 定义属性编辑器的时候使用的是PropertyEditorRegistry的registerCustomEditor方法,而这里使 用的是ConfigurableListableBeanFactory的addProperty Editor Registrar方法。我们不妨深入探索 一下ResourceEditorRegistrar的内部实现,在ResourceEditorRegistrar中,我们最关心的方法是 registerCustomEditors。

在doRegisterEditor 函数中,可以看到在之前提到的自定义属性中使用的关键代码:
registry.registerCustomEditor(requiredType, editor),回过头来看ResourceEditorRegistrar 类的registerCustomEditors 方法的核心功能,其实无非是注册了一系列的常用类型的属性编辑器, 例如,代码doRegisterEditor(registry, Class.class, new ClassEditor(classLoader))实现的功能就是注册Class 类对应的属性编辑器。那么,注册后,一旦某个实体bean 中存在一些Class 类型的属性,那么Spring 会调用C l assEditor 将配置中定义的String 类型转换为Class 类型并进行赋值。
分析到这里,我们不禁有个疑问,虽说ResourceEditorRegis位ar 类的registerCustomEditors 方法实现了批量注册的功能,但是beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(白is,getEnvironment()))仅仅是注册了ResourceEditorRegistrar 实例,却并没有调用ResourceEditorRegistrar的registerCustomEditors 方法进行注册, 那么到底是在什么时候进行注册的呢? 进一步查看ResourceEditorRegistrar 的registerCustomEditors 方法的调用层次结构,如图所示。

发现在AbstractBeanFactory 中的registerCustomEditors 方法中被调用过,继续查看AbstractBeanFactory 中的registerCustomEditors 方法的调用层次结构,如图所示。

其中我们看到一个方法是我们熟悉的,就是AbstractBeanFactory类中的initBeanWrapper 方法,这是在bean初始化时使用的一个方法,之前已经使用过大撮的篇幅进行讲解,主要是在将BeanDefinition转换为BeanWrapper后用于对属性的填充。到此,逻辑已经明了,在bean 的初始化后会调用ResourceEditorRegistrar的registerCustomEditors方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让Spring使用这些编辑器进行属性的解析了。

既然提到了BeanWrapper, 这里也有必要强调下,Spring中用于封装bean的是Beru1 Wrapper 类型,而它又间接继承了PropertyEditorRegistry类型,也就是我们之前反复看到的方法参数 PropertyEditorRegistry registry, 其实大部分清况下都是BeanWrapper, 对于BeanWrapper在 Spring中的默认实现是BeanWrapperlmpl, 而BeanWrapperlmpl除了实现BeanWrapper接口外 还继承了ProrertyEditorRegistrySupport, 在PropertyEditorRegistrySupport中有这样一个方法:

具体的调用方法我们就不去深究了,但是至少通过这个方法我们已经知道了在Spring中定 义了上面一系列常用的属性编辑器使我们可以方便地进行配置。如果我们定义的bean中的某 个属性的类型不在上面的常用配置中的话,才需要我们进行个性化属性编辑器的注册。

添加ApplicationContextAwareProcess可处理器

了解了属性编辑器的使用后,接下来我们继续通过AbstractApplicationContext的prepareBeanFactory方法的主线来进行函数跟踪。对于beanfactory.addBeanPostProcessor(new Application­ContextAwareProcessor(this))其实主要目的就是注册个BneaPostProcessor, 而真正的逻辑还是在 ApplicationContextAwareProcessor中。

ApplicationContextAwareProcessor实现BeanPostProcessor接口,我们回顾下之前讲过的内 容,在bean实例化的时候,也就是Spring激活bean的init-method的前后,会调用BeanPost­Processor的postProcessBeforelnitialization方法和postProcessAfterlnitialization方法。同样,对 于ApplicationContextAwareProcessor我们也关心这两个方法。

对于postProcessAfterlnitialization方法,在ApplicationContextAwareProcessor中并没有做过 多逻辑处理。

postProcessBeforelnitialization方法中调用了invokeAwarelnterfaces。从invokeAwareinterfaces 方法中,我们或许已经或多或少了解了Spring的用意,实现这些Aware接口的bean在被初始 化之后,可以取得一些对应的资源。

设置忽略依赖

当Spring将ApplicationContextAwareProcessor注册后,那么在invokeAwarelnterfaces方法中间 接调用的Aware类已经不是普通的bean了,如ResourceLoaderAware、ApplicationEventPublisherAware等,那么当然需要在Spring做bean的依赖注入的时候忽略它们。而ignoreDependency Interface 的作用正是在此。

注册依赖

Spring中有了忽略依赖的功能,当然也必不可少地会有注册依赖的功能。

当注册了依赖解析后,例如当注册了对BeanFactory.class的解析依赖后,当bean的属性注 入的时候,一旦检测到属性为BeanFactory类型便会将beanFactory的实例注入进去。

BeanFactory的后处理

BeanFactory作为Spring中容器功能的基础,用于存放所有已经加载的bean, 为了保证程 序上的高可扩展性,Spring针对BeanFactory做了大量的扩展,比如我们熟知的PostProcessor 等都是在这里实现的。

激活注册的Bean FactoryPostProcessor

正式开始介绍之前我们先了解下BeanFactoryPostProcessor的用法。

BeanFactory PostProcessor接口跟BeanPostProcessor类似,可以对bean的定义(配置元数 据)进行处理。也就是说,Spring IoC容器允许BeanFactory PostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并有可能修改它。如果你愿意,你可以配置多个 BeanFactoryPostProcessor。你还能通过设置"order"属性来控制BeanFactoryPostProcessor的执行次序(仅当BeanFactoryPostProcessor实现了Ordered接口时你才可以设置此属性,因此在实现 BeanFactoryPostProcessor时,就应当考虑实现Ordered接口)。请参考BeanFactoryPostProcessor 和Ordered接口的JavaDoc以获取更详细的信息。

如果你想改变实际的bean实例(例如从配置元数据创建的对象),那么你最好使用 BeanPostProcessor。同样地,BeanFactoryPostProcessor的作用域范围是容器级的。它只和你所使用的容器有关。如果你在容器中定义一个BeanFactoryPostProcessor, 它仅仅对此容器中的 bean进行后置处理。BeanFactoryPostProcessor不会对定义在另一个容器中的bean进行后置处 理,即使这两个容器都是在同一层次上。在Spring中存在对于BeanFactoryPostProcessor的典型应用,比如PropertyPlaceholderConfigurer。

BeanFactoryPostProcessor的典型应用

PropertyPlaceholderConfigurer 有时候,阅读Spring的Bean描述文件时,你也许会遇到类似如下的一些配置:

在这个bean中指定了配置文件为config/bean. properties。到这里似乎找到间题的答案了, 但是其实还有个间题。这个"mesHandler"只不过是Spring框架管理的一个bean, 并没有被别的bean或者对象引用,Spring的BeanFactory是怎么知道要从这个Bean中获取配置信息的呢?

查看层级结构可以看出PropertyPlaceholderConfigurer这个类间接继承了BeanFactory­PostProcessor接口。这是一个很特别的接口,当Spring加载任何实现了这个接口的bean的配 置时,都会在bean工厂载入所有bean的配置之后执行postProcessBeanFactory方法。在 PropertyResourceConfigurer类中实现了postProcessBeanFactory方法,在方法中先后调用了 mergeProperties、convertProperties、processProperties这3个方法,分别得到配置,将得到的配 置转换为合适的类型,最后将配置内容告知BeanFactory。

正是通过实现BeanFactoryPostProcessor接口,BeanFactory会在实例化任何bean之前获得配置信息,从而能够正确解析bean描述文件中的变量引用。

使用自定义Bean FactoryPostProcessor

我们以实现一个BeanFactoryPostProcessor, 去除潜在的“流氓“属性值的功能来展示自定 义BeanFactoryPostProcessor的创建及使用,例如bean定义中留下bollocks这样的字眼。

通过ObscenityRemovingBeanFactoryPostProcessor Spring很好地实现了屏蔽掉obscenties定 义的不应该展示的属性。

激活BeanFactoryPostProcessor

了解了BeanFactoryPostProcessor的用法后便可以深入研究BeanFactoryPostProcessor的调 用过程了。

从上面的方法中我们看到,对于BeanFactoryPostProcessor的处理主要分两种情况进行,一个是对于BeanDefinitionRegistry类的特殊处理,另一种是对普通的 BeanFactoryPostProcessor进行处 理。而对于每种情况都需要考虑硬编码注入注册的后处理器以及通过配置注入的后处理器。

对于BeanDefinitionRegistry类型的处理类的处理主要包括以下内容。

1 对于硬编码注册的后处理器的处理,主要是通过AbstractApplicationContext中的添加处理器方法addBeanFactoryPostProcessor进行添加。

添加后的后处理器会存放在BeanFactoryPostProcessors中,而在处理BeanFactoryPostProcessor 时候会首先检测BeanFactoryPostProcessors是否有数据。当然,BeanDefinitionRegistryPostProcessor 继承自BeanFactoryPostProcessor, 不但有BeanFactoryPostProcessor的特性,同时还有自己定义 的个性化方法,也需要在此调用。所以,这里需要从BeanFactoryPostProcessors中挑出 BeanDefinitionRegistry PostProcessor的后处理器,并进行其postProcessBeanDefinitionRegistry方 法的激活。

2 记录后处理器主要使用了3个List完成。

registryPostProcessors: 记录通过硬编码方式注册的BeanDefinitionRegistryPostProcessor 类型的处理器。

regularPostProcessors: 记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处 理器。

registryPostProcessorBeans: 记录通过配置方式注册的BeanDefinitionRegistryPostProcessor 类型的处理器。

3 对以上所记录的List中的后处理器进行统一调用BeanFactoryPostProcessor的 postProcessBeanFactory方法。

4 对BeanFactoryPostProcessors中非BeanDefinitionRegistryPostProcessor类型的后处理器进行 统一的BeanFactoryPostProcessor的postProcessBeanFactory方法调用。

5 普通BeanFactory处理。

BeanDefinitionRegistryPostProcessor只对BeanDefinitionRegistry类型的ConfigurableListable­BeanFactory有效,所以如果判断所示的BeanFactory并不是BeanDefinitionRegistry, 那么便可 以忽略BeanDefinitionRegistryPostProcessor, 而直接处理BeanFactoryPostProcessor, 当然获取 的方式与上面的获取类似。

这里需要提到的是,对于硬编码方式手动添加的后处理器是不需要做任何排序的,但是在配置文件中读取的处理器,Sping并不保证读取的顺序。所以,为了保证用户的调用顺序的要 求,Spring对于后处理器的调用支持按照PriorityOrdered或者Ordered的顺序调用。

注册BeanPostProcessor

上文中提到了BeanFacotoryPostProcessors的调用,现在我们来探索下BeanPostProcessor, 但是这里并不是调用,而是注册。真正的调用其实是在bean的实例化阶段进行的。这是一个 很重要的步骤,也是很多功能BeanFactory不支持的重要原因。Spring中大部分功能都是通过 后处理器的方式进行扩展的,这是Spring框架的一个特性,但是在BeanFactory中其实并没有 实现后处理器的自动注册,所以在调用的时候如果没有进行手动注册其实是不能使用的。但是 在ApplicationContext中却添加了自动注册功能,如自定义这样一个后处理器:

配合源码以及注释,在registerBeanPostProcessors方法中所做的逻辑相信大家已经很清楚 了,我们再做一下总结。

首先我们会发现,对于BeanPostProcessor的处理与BeanFactoryPostProcessor的处理极为 相似,但是似乎又有些不一样的地方。经过反复的对比发现,对于BeanFactoryPostProcessor 的处理要区分两种情况,一种方式是通过硬编码方式的处理,另一种是通过配置文件方式的处理。 那么为什么在BeanPostProcessor的处理中只考虑了配置文件的方式而不考虑硬编码的方式呢? 提出这个问题,还是因为读者没有完全理解两者实现的功能。对于BeanFactoryPostProcessor 的处理,不但要实现注册功能,而且还要实现对后处理器的激活操作,所以需要载入配置中的 定义,并进行激活;而对于BeanPostProcessor并不需要马上调用,再说,硬编码的方式实现的功能是将后处理器提取并调用,这里并不需要调用,当然不需要考虑硬编码的方式了,这里的 功能只需要将配置文件的BeanPostProcessor提取出来并注册进入beanFactory就可以了。

对于BeanFactory的注册,也不是直接注册就可以的。在Spring中支持对于BeanPost­Processor的排序,比如根据PriorityOrdered进行排序、根据Ordered进行排序或者无序,而Spring 在BeanPostProcessor的激活顺序的时候也会考虑对于顺序的问题而先进行排序。

这里可能有个地方读者不是很理解,对于internalPostProcessors中存储的后处理器也就是 MergedBeanDefinitionPostProcessor类型的处理器,在代码中似乎是被重复调用了,如:

可以看到,在registerBeanPostProcessors方法的实现中其实已经确保了beanPostProcessor 的唯一性,个人猜想,之所以选择在registerBeanPostProcessors中没有进行重复移除操作或许 是为了保持分类的效果,使逻辑更为清晰吧。

初始化消息资源

在进行这段函数的解析之前,我们同样先来回顾Spring国际化的使用方法。

假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界 面一一这便是典型的il8n国际化间题。对于有国际化要求的应用系统,我们不能简单地采用硬 编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处 理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定 的目录中,由系统自动根据客户端语言选择适合的资源文件。

“国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特定类型的本地化信 息,它们分别是“语言类型'和“国家/地区的类型'。如中文本地化信息既有中国大陆地区的中 文,又有中国台湾地区、中国香港地区的中文,还有新加坡地区的中文。Java通过java.util.Locale 类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。

java.util.Locale是表示语言和国家/地区信息的本地化类,它是创建国际化应用的基础。下面给出几个创建本地化对象的示例:

JDK的java.util包中提供了几个支持本地化的格式化操作工具类: NumberFormat、 DateFormat、MessageFormat, 而在Spring中的国际化资源操作也无非是对于这些类的封装操 作,我们仅仅介绍下MessageFormat的用法以帮助大家回顾:

Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。

MessageSource分别被HierarchicalMessageSource和ApplicationContext接口扩展,这里我们主要看一下HierarchicalMessageSource接口的几个实现类,如图所示。

HierarchicalMessageSource接口最重要的两个实现类是ResourceBundleMessageSource和 Reloadab leResourceBundleMessageSource。它们基于Java的ResourceBundle基础类实现,允许 仅通过资源名加载国际化资源。ReloadableResourceBundleMessageSource提供了定时刷新功能, 允许在不重启系统的情况下,更新资源的信息。StaticMessageSource主要用于程序测试,它允 许通过编程的方式提供国际化信息。而DelegatingMessageSource是为方便操作父MessageSource 而提供的代理类。仅仅举例ResourceBundleMessageSource的实现方式。

了解了Spring国际化的使用后便可以进行源码的分析了。

在initMessageSource中的方法主要功能是提取配置中定义的messageSource, 并将其记录 在Spring的容器中,也就是AbstractApplicationContext中。当然,如果用户未设置资源文件的话,Spring中也提供了默认的配置DelegatingMessageSource。

在initMessageSource中获取自定义资源文件的方式为BeanFactory.getBean(MESSAGE_ SOURCE—BEAN—NAME, MessageSource.class), 在这里Spring使用了硬编码的方式硬性规定了 子定义资源文件必须为message, 否则便会获取不到自定义资源配置,这也是为什么之前提到 Bean的id如果部位message会抛出异常。

通过读取并将自定义资源文件配置记录在容器中,那么就可以在获取资源文件的时候直接使用了,例如,在AbstractApplicationContext中的获取资源文件属性的方法:

初始化ApplicationEventMuliticaster

在讲解Spring的时间传播器之前,我们还是先来看一下Spring的事件监听的简单用法。 

当程序运行时,Spring会将发出的TestEvent事件转给我们自定义的TestListener进行进一 步处理。

或许很多人一下子会反映出设计模式中的观察者模式,这确实是个典型的应用,可以在比 较关心的事件结束后及时处理。那么我们看看ApplicationEventMulticaster是如何被初始化的, 以确保功能的正确运行。

initApplicationEventMulticaster的方式比较简单,无非考虑两种情况。

如果用户自定义了事件广播器,那么使用用户自定义的事件广播器。

如果用户没有自定义事件广播器,那么使用默认的ApplicationEventMulticaster。

按照之前介绍的顺序及逻辑,我们推断,作为广播器,一定是用于存放监听器并在合适的 时候调用监听器,那么我们不妨进入默认的广播器实现SimpleApplicationEventMulticaster来一 探究竟。

其中的一段代码是我们感兴趣的。

可以推断,当产生Spring事件的时候会默认使用SimpleApplicationEventMulticaster的

multicastEvent来广播事件,遍历所有监听器,并使用监听器中的onApplicationEvent方法来进 行监听器的处理。而对于每个监听器来说其实都可以获取到产生的事件,但是是否进行处理则 由事件监听器来决定。

注册监听器

之前在介绍Spring的广播器时反复提到了事件监听器,那么在Spring注册监听器的时候又做了哪些逻辑操作呢?

初始化非延迟加载单例

完成BeanFactory的初始化工作,其中包括ConversionService的设置、配置冻结以及非延迟加载的bean的初始化工作。

首先我们来了解一下ConversionService类所提供的作用。

ConversionService的设置

之前我们提到过使用自定义类型转换器从Su·ing转换为Pate的方式,那么,在Spring中 还提供了另一种转换方式:使用Converter。同样,我们使用一个简单的示例来了解下Converter 的使用方式。

通过以上的功能我们看到了Converter以及ConversionService提供的便利功能,其中的配 置就是在当前函数中被初始化的。

冻结配置

冻结所有的bean定义,说明注册的bean定义将不被修改或进行任何进一步的处理。

初始化非延迟加载

ApplicationContext实现的默认行为就是在启动时将所有单例bean提前进行实例化。提 前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的单例 bean。通常情况下这是一件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。而这个实例化的过程就是在finishBeanFactoryInitialization中 完成的。

finishRefresh

在Spring中还提供了Lifecycle接口,Lifecycle中包含start/stop方法,实现此接口后Spring会 保证在启动的时候调用其start方法开始生命周期,并在Spring关闭的时候调用stop方法来结束生 命周期,通常用来配置后台程序,在启动后一直运行(如对MQ进行轮询等)。而ApplicationContext 的初始化最后正是保证了这一功能的实现。

initLifecycleProcessor

当ApplicationContext启动或停止时,它会通过LifecycleProcessor来与所有声明的bean的 周期做状态更新,而在LifecycleProcessor的使用前首先需要初始化。

onRefresh

启动所有实现了Lifecycle接口的bean。

publishEvent

当完成ApplicationContext初始化的时候,要通过Spring中的事件发布机制来发出Context­RefreshedEvent事件,以保证对应的监听器可以做进一步的逻辑处理。

 

原文链接:https://blog.csdn.net/xushiyu1996818/article/details/117637169



所属网站分类: 技术文章 > 博客

作者:java小王子

链接:http://www.javaheidong.com/blog/article/222016/97cb62686134596b768a/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

20 0
收藏该文
已收藏

评论内容:(最多支持255个字符)