《Spring揭秘》学习摘要 Part3

7 篇文章 0 订阅
4 篇文章 0 订阅

容器背后的密码

        Spring的IoC容器所起的作用,它会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。

Spring的IoC容器实现以上功能的过程,基本上可以按照类似的流程划分为两个阶段,即容器的启动阶段和Bean的实例化阶段。

Spring的IoC容器在实现的时候,充分运用了这两个实现阶段的不同特点,在每个阶段都加入了容器的扩展点,以便我们可以根据具体场景的需要加入自定义的扩展逻辑。

1、容器启动阶段

加载配置.......

分析配置信息......

装备到BeanDefinition......

其他后处理......

         容器启动伊始,会通过某种途径加载Configuration MetaData。容器需要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData进行解析和分析,并将分析后的信息组编为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry。这样容器的启动工作就完成了。

2、Bean实例化阶段

实例化对象......

装配依赖......

生命周期回调......

对象其他处理......

注册回调接口......

在该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,那会根据注册的BeanDefinition所提供的信息实例化被请求的对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕之后,容器会立即将其返回请求方使用。


Spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制。该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。这就相当于在容器实现的第一个阶段最后加入一道工序,让我们对最终的BeanDefinition做一些额外的操作。

如果需要自定义实现BeanFactoryPostProcessor,通常我们需要实现org.springframework.beans.factory.config.BeanFactoryPostProcessor接口。Spring已经提供了几个 现成的BeanFactoryPostProcessor实现类,所以,很少时候需要我们自己动手实现某个BeanFacotryPostProcessor。其中,org.springframework.beans.factory.PropertyPlaceholderConfigurer和org.springframework.beans.factory.config.PropertyOverrideConfigurer是两个比较常用的BeanFactoryPostProcessor。

对于BeanFactory来说,我们需要用手动方式应用到所有的BeanFactoryPostProcessor:

ConfigurableListableBeanFactory beanFatory = new XmlBeanFactory(new ClassPathResource("..."));

PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer();

propertyPostProcessor.setLocation(new ClassPathResource("..."));

propertyPostProcessor.postProcessBeanFactory(beanFactory);

由于ApplicationContext来说,ApplicationContext会自动识别配置文件中的BeanFactoryPostProcessor并应用它,仅需要在XML配置文件中将这些BeanFactoryPostProcessor简单配置一下即可:

	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>conf/jdbc.properties</value>
				<value>conf/jmail.properties</value>
			</list>
		</property>
	</bean>

PropertyPlaceholderConfigurer允许我们在XML配置文件中使用占位符(PlaceHolder),并将这些占位符所代表的资源单独配置到简单的properties文件中来加载。

当BeanFactory在第一阶段加载完成所有配置信息时,BeanFactory中保存的对象属性信息还是以占位符的形式存在。当ProperyPlaceholderConfigurer作为BeanFactoryPostProcessor被应用时,它会使用properties配置文件中的配置信息来替换相应的BeanDefinition中占位符所表示的属性值。这样,当进入容器实现的第二阶段实例化bean时,bean定义中的属性值就是最终替换完成了。

PropertyPlaceholderConfigurer不单会从其配置的properties文件中加载配置项,同时还会检查java的System类中的Properties,可以通过setSystemPropertiesMode()或者setSystemPropertiesModeName()来控制是否加载或者覆盖System相应的Properties的行为。


PropertyOverrideConfigurer对容器中配置的任何bean信息的property信息进行覆盖替换。例如,我们配置了dataSource bean的maxActive的值为100,我们可以通过PropertyOverrideConfigurer在其相应的properties文件中做如下的配置,就可以把100这个值给覆盖掉,如将其值配置为200:

dataSource.maxActive=200

如果需要对容器中的某些bean定义的property信息进行覆盖,我们需要按照如下规则提供一个PropertyOverrideConfigurer使用的配置文件:

beanName.propertyName=value

<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">

<property name="localhost" value="pool-adjustment.properties" />

</bean>

pool-adjustment.properties中没有提供的配置项将继续使用原来XML配置中的默认值。

当容器中配置的多个PropertyOverrideConfigurer对同一个bean定义的同一个property值进行处理的时候,最后一个将会生效。

     配置在properties文件中的信息通常都以明文表示,PropertyOverrideConfigurer的父类PropertyOverrideConfigurer提供了一个protected类型的方法convertPropertyValue,允许子类覆盖这个方法对相应的配置项进行转换,如对加密后的字符串解密之后再覆盖到相应的bean定义中。当然,既然PropertyPlaceholderConfigurer也同样继承了PropertyResourceConfigurer,我们也可以针对PropertyPlaceholderConfigurer应用类似的功能。


Bean的一生

容器启动后,并不会马上就实例化相应的bean定义。容器现在仅仅拥有所有对象的BeanDefinition来保存实例化阶段将要用的必要信息。只有当请求方通过BeanFactory的getBean()方法来请求某个对象实例的时候,才有可能触发Bean实例化阶段的活动。getBean()方法可以被客户端显示的调用,也可以在容器内被隐式的调用:

  • 对于BeanFactory来说,对象实例化默认采用延迟加载初始化。当对象A被请求而需要第一次实例的时候,rugged它所依赖的对象B之前同样没有被实例化,那么容器会先实例化对象A所依赖的对象。这种情况是容器内部的隐式调用getBean()方法。
  • ApplicationContext启动之后会实例化所有的bean定义,但ApplicationContext在实现的过程中依然遵循Spring容器实现流程的两个阶段,只不过他会在启动阶段的活动完成之后,紧接着调用注册到该容器的所有bean定义的实例化方法getBean()。
只有当对应的某个bean定义的getBean()方法第一次被调用是,不管是显示还是隐式的,Bean实例化阶段的活动才会被触发,第二次被调用则会直接返回容器缓存的第一次调用实例化完的对象实例(prototype类型的bean除外)。当getBean()方法内部发现该bean定义之前还没有被实例化之后,会通过creatBean()方法来进行具体的对象实例化。实例化过程:
实例化bean对象——>设置对象属性——>检查Aware相关接口并设置相关依赖——>BeanPostProcessor前置处理——>检查是否是InitializingBean以决定是否调用afterPropertiesSet方法——>检查是否配置有自定义的init-method——>BeanPostProcessor后置处理——>注册必要的Destruction相关回调接口——>是否实现DisposableBean接口——>是否配置有自定义的destroy方法
Spring容器将对其所管理的对象全部给予统一的生命周期管理,这些被管理的对象完全摆脱了原来那种new完后被使用,脱离作用域后即被回收的命运。

容器在内部实现的时候,采用策略模式来决定采用何种方式初始化bean实例。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。默认情况下,容器内部采用的是CglibSubclassingInstantiationStrategy策略实现类来动态生成某个类的子类。该策略实现是org.springframework.beans.factory.support.InstantiationStrategy接口实现子类。
容器只要根据相应bean定义的BeanDefinition取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不同的bean定义类型,就可以返回实例化完成的对象实例。但是,不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。BeanWrapper接口通常在Spring框架内部使用,它有一个实现类org.springframework.beans.BeanWapperImpl。其作用是对某个bean进行包裹,然后对这个包裹的bean进行操作,比如设置或者获取bean的相应属性值。使用BeanWapper对bean实例进行操作很方便,可以免去直接使用Java反射API操作对象实例的烦琐。

当对象实例化完成并且相关属性以及依赖设置完成之后,Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义。如果是,则将这些Aware接口定义中规定的依赖注入给当前对象实例。

BeanPostProcessor的概念容易与BeanFactoryPostProcessor的概念混淆。但只要记住BeanPostProcessor是存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器初始化阶段,这两个概念就比较容易区分了。
与BeanFactoryPostProcessor通常会处理容器内所有符合条件的BeanDefinition类似,BeanPostProcessor会处理容器内所有符合条件的实例化后的对象实例。该接口声明了两个方法,分别在不同的时机执行,具体可以参加BeanPostProcessor源码和其相关JavaDoc。通常比较常见的使用BeanPostProcessor的场景,是处理标记接口实现类,或者为当前对象提供代理实现。ApplicationContext对应的那些Aware接口实际上就是通过BeanPostProcessor前置处理这一步实现的。Spring 的AOP则更多的使用BeanPostProcessor来为对象生成相关的代理对象。BeanPostProcessor是容器提供的对象实例化阶段的强有力的扩展点。对于ApplicationContext容器来说,直接将相应的BeanPostProcessor实现类通过通常的XML配置文件配置一下即可。ApplicationContext容器会自动识别并加载注册到容器的BeanPostProcessor。合理利用BeanPostProcessor这种Spring的容器扩展机制,将可以构造强大而灵活的应用系统。

org.springframework.beans.factory.InitializingBean是容器内部广泛使用的一个对象生命周期标识接口,该接口很简单,其作用在于,在对象实例化过程调用过“BeanPostProcessor的前置处理”之后,会接着检查当前对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet()方法进一步调整对象实例化的状态。虽然该接口在Spring内部广泛使用,但如果真的让我们的业务对象实现这个接口,则显得Spring容器比较具有入侵性。所有,Spring还提供了另外一种方式来指定自定义的对象初始化操作,那就是在XML配置的时候,使用<bean>的init-method属性。通过init-method,系统中业务对象的自定义初始化操作可以以任何方式命名,而不再受制于InitialzingBean的afterPropertiesSet()。还可以通过最顶层的<beans>的default-init-method统一指定这一init()方法名。

当所有的一切,完成之后,Spring容器将检查singleton类型的bean实例,看其是否实现了org.springframework.beans.factory.DispossableBean接口。或者其对应的bean定义是否通过<bean>的destroy-method属性指定了自定义的对象销毁方法。如果是,就会为该实例注册一个用于对象销毁的callback,以便在这些singleton类型的对象实例销毁之前,执行销毁逻辑。最常见到的该功能的使用场景就是在Spring容器中注册数据库连接池,在系统退出后,连接池应该关闭,以释放相应的资源。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值