Spring揭秘 读书笔记 五 容器的启动

时间:2021-01-27 10:24:25
Spring的IoC容器所起的作用,就是生产bean,并维持bean间的依赖关系。它会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。 
IoC实现上面要求的过程,可以分解为两步:
Spring揭秘 读书笔记 五 容器的启动
启动阶段分析:
1 加装资源文件
2 通过工具类(BeanDefinitionReader),分析资源文件
3 生成对应的BeanDefinition
4 将BeanDefinition注册到BeanDefinitionRegistry。
实例化阶段分析:
显示地调用container.getBean会触发bean的实例化
当实例化A的时候发现A依赖B,那么就会触发B的实例化。


当实例化某个对象的时候,首先会检查对象是否已经初始化,如果没有的话,初始化。
如果有依赖的对象,就注入之。
如果对象实现了某些回调接口,就根据接口装备它。


插手容器的启动

Spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制。该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。这就相当于在容器实现的第一阶段最后加入一道工序,让我们对最终的BeanDefinition做一些额外的操作,比如修改其中bean定义的某些属性,为bean定义增加其他信息等。 
使用BeanFactoryPostProcessor的方式,就是实现这个接口,然后在实现类里写逻辑。
我们先看看这个接口
public interface BeanFactoryPostProcessor {


	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;


}
上面的英文大家能看懂吧。allows for overriding or adding properties even to eager-initializing beans.
但是,因为Spring已经提供了几个现成的BeanFactoryPostProcessor实现类,所以,大多时候,我们很少自己去实现某个BeanFactoryPostProcessor。其中,org.springframework.beans. factory.config.PropertyPlaceholderConfigurer和org.springframework.beans.factory.config.Property OverrideConfigurer是两个比较常用的BeanFactoryPostProcessor。


PropertyPlaceholderConfigurer

通常情况下,我们不想将类似于系统管理相关的信息同业务对象相关的配置信息混杂到XML配置文件中,以免部署或者维护期间因为改动繁杂的XML配置文件而出现问题。我们会将一些数据库连接信息、邮件服务器等相关信息单独配置到一个properties文件中,这样,如果因系统资源变动的话,只需要关注这些简单properties配置文件即可。 
看例子:
容器使用BeanFactory
//声明将被后处理的BeanFactory实例 
ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("...")); 
//声明要使用的BeanFactoryPostProcessor 
PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer(); 
propertyPostProcessor.setLocation(new ClassPathResource("...")); 
//执行后处理操作 
propertyPostProcessor.postProcessBeanFactory(beanFactory); 
使用Application
只需在xml文件中加上
... 
<beans> 
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
   <property name="locations"> 
   <list> 
      <value>conf/myjdbc.properties</value> 
      <value>conf/mail.properties</value> 
     </list> 
   </property> 
  </bean> 
   ... 
</beans>  
使用了propertyPlaceHolderConfigure后,xml中dataSource如下
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
  <property name="url"> 
   <value>${jdbc.url}</value> 
  </property> 
  <property name="driverClassName"> 
   <value>${jdbc.driver}</value> 
  </property> 
  <property name="username"> 
   <value>${jdbc.username}</value> 
  </property> 
  <property name="password"> 
   <value>${jdbc.password}</value> 
  </property> 
  <property name="testOnBorrow"> 
   <value>true</value> 
  </property> 
  <property name="testOnReturn"> 
   <value>true</value> 
  </property> 
  ...
</bean>    

##myjdbc.properties 
jdbc.url=jdbc:mysql://server/MAIN?useUnicode=true&characterEncoding=ms932&failOverReadOnly=false&roundRobinLoadBalance=true 
jdbc.driver=com.mysql.jdbc.Driver 
jdbc.username=your username 
jdbc.password=your password 


xml中的${jdbc.url},就用properties中的jdbc.url相对应。

没有什么好讲的,就是这些死内容而已。


PropertyOverrideConfigurer 

PropertyOverrideConfigurer干的事,就是修改从xml(无论是否包含propertyPlaceHolderConfigure的properties)加载过来的信息。
下面是针对dataSource定义给出的PropertyOverrideConfigurer的propeties文件配置信息:   
##文件名是adjusment.properties 
dataSource.minEvictableIdleTimeMillis=1000 
dataSource.maxActive=50 
注意这个dataSource指的是beanName。***
这样,当按照如下代码,将PropertyOverrideConfigurer加载到容器之后,dataSource原来定
义的默认值就会被pool-adjustment.properties文件中的信息所覆盖:   
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"> 
  <property name="location" value="adjusment.properties"/> 
< /bean> 
PropertyOverrideConfigurer使用BeanFactory的方式,我就不再赘述了。
说实话,我并不觉得PropertyOverrideConfigurer有什么卵用。


CustomEditorConfigurer 

java是强类型的一种语言:int,String,String[],Person等等等等。
而我们配置的xml,里面都是字面量,也就是说,spring拿到的原始数据全部是字符串。
那么,spring是怎么把字符串转成各种各样的类型呢?
用工具呗?
用什么工具?
CustomEditorConfigurer
Spring内部通过JavaBean的PropertyEditor来帮助进行String类型到其他类型的转换工作。只要为每种对象类型提供一个PropertyEditor,就可以根据该对象类型取得与其相对应的PropertyEditor来做具体的类型转换。Spring容器内部在做具体的类型转换的时候,会采用JavaBean框架内默认的PropertyEditor搜寻逻辑。
同时,Spring框架还提供了自身实现的一些Property-Editor。
  StringArrayPropertyEditor。该PropertyEditor会将符合CSV格式的字符串转换成String[]数组的形式,默认是以逗号(,)分隔的字符串,但可以指定自定义的字符串分隔
符。ByteArrayPropertyEditor、CharArrayPropertyEditor等都属于类似功能的Property- Editor,参照Javadoc可以取得相应的详细信息。 
  ClassEditor。根据String类型的class名称,直接将其转换成相应的Class对象,相当于通过Class.forName(String)完成的功效。可以通过String[]数组的形式传入需转换的值,以达到与提供的ClassArrayEditor同样的目的。 
  FileEditor。Spring提供的对应java.io.File类型的PropertyEditor。同属于对资源进行定位的PropertyEditor还有InputStreamEditor、URLEditor等。 
  LocaleEditor。针对java.util.Locale类型的PropertyEditor,格式可以参照Local-eEditor和Locale的Javadoc说明。 
  PatternEditor。针对JavaSE1.4之后才引入的java.util.regex.Pattern的Property-Editor,格式可以参照java.util.regex.Pattern类的Javadoc。
更多的关于CustomEditorConfigurer的资料参考 http://blog.csdn.net/zhoudaxia/article/details/36247883#t4

注意

按照JavaBeans的规范,JavaBeans的基础设施会在JavaBean相同类包下查找是否存在<JavaBean>Editor的类,如果存在,自动使用<JavaBean>Editor作为该JavaBean的PropertyEditor。

  如com.baobaotao.domain.UserEditor会自动成为com.baobaotao.domain.User对应的PropertyEditor。Spring也支持这个规范,也即如果采用这种规约命令PropertyEditor,就无须显式在CustomEditorConfigurer中注册了,Spring将自动查找并注册这个PropertyEditor。(甚至不用在xml中使用bean这个标签声明一个UserEditor)



如果是使用的是BeanFactory的实现,如XmlBeanFactory,就得通过硬编码的方式来应用CustomEditorConfigurer到容器中了。

XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource
CustomEditorConfigurer ceConfigurer = new CustomEditorConfigurer(); 
Map customerEditors = new HashMap(); 
customerEditors.put(java.util.Date.class, new DatePropertyEditor()); //以后凡是string转Date型的 都用DatePropertyEditor
ceConfigurer.setCustomEditors(customerEditors); 
ceConfigurer.postProcessBeanFactory(beanFactory);   

DatePropertyEditor如下

Spring揭秘 读书笔记 五 容器的启动

如果我们想把某种格式的字符串转化为Person类,那么我们可以采用上面的方式,但是如果我们想把spring转化成date,我们又不能新建一个package名字叫java.util。
所以我们得让容器知道CustomEditorConfigurer的实现。

使用硬编码的方式,上面已经说了

如果是采用ApplicationContext,xml如下:

Spring揭秘 读书笔记 五 容器的启动

使用时:

Spring揭秘 读书笔记 五 容器的启动

大家注意,我给DateFoo传递的事一个"符合"DatePropertyEdit中datePattern格式的字符串"2007/10/16",这个字符串就是DatePropertyEdit中setAsTest方法中的参数

通过CustomerEditorConifgure,Spring已经知道了凡是String要转化成Date都去找datePropertyEdit... 后面的大家应该都能看懂了

不过在spring2.0之后,提倡使用,比较提倡使用propertyEditorRegistrars属性来指定自定义的PropertyEditor。这样一来我们就多了一步,实现PropertyEditorRegistrar 接口


Spring揭秘 读书笔记 五 容器的启动

然后在xml里这样注册:

Spring揭秘 读书笔记 五 容器的启动

再次注意:

我们知道有了上面的配置后,string转成date就会用datePrpertyEditor这个类

但是,这个类是什么时候被调用的呢?

答案是: 在getBean的时候

感谢glt

参考资料

http://blog.csdn.net/zhoudaxia/article/details/36247883