(spring-第9回【IoC基础篇】)BeanFactoryPostProcessor,实例化Bean之前的第二大利器

继承结构图如上。在加载XML,注册bean definition之后,在实例化bean definition之前,必要的时候要用到BeanFactoryPostProcessor。它负责把XML中有些占位符式的属性还原成真实值。意思是说,有时候,XML中<bean>的属性值不固定,会随着外界因素变化,这时候,在<bean>中配置占位符,而另外定义一个属性文件来控制<bean>的属性。比如下面是一个数据库连接的XML配置:

1 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
2         destroy-method="close"
3         p:driverClassName="${driverClassName}"
4         p:url="${url}"
5         p:username="${userName}"
6         p:password="${password}" />

下面定义了一个jdbc.properties:

1 dbName=sampledb
2 driverClassName=com.mysql.jdbc.Driver
3 url=jdbc:mysql://localhost:3306/${dbName}
4 #userName=root
5 #password=1234

从jdbc.properties读取属性值,然后赋值给<bean> xml中的类似${password}的占位符。这时就要用到BeanFactoryPostProcessor的实现类。在这个例子中,我们可以使用PropertyPlaceHolderConfigurer。

一个例子:

首先,一共四个文件:

Car.java:最简单的bean类,源码如下:

 1 public class Car {
 2     private String name;
 3     private String color;
 4     private double price;
 5
 6     private Log log=LogFactory.getLog(Car.class);
 7
 8     public Car(){
 9         //name="宝马";
10         log.info("调用了Car的构造函数,实例化了Car..");
11     }
12
13
14     public String getName() {
15 。。。
16 。。。

后面省略了getter和setter方法。

Main.java,简单的启动类,如下:

 1 public class Main {
 2     private static Log log=LogFactory.getLog(Main.class);
 3
 4     public static void main(String args[]){
 5         ApplicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/test2/*.xml");
 6         Car car = ctx.getBean("car",Car.class);
 7         log.info(car.toString());
 8     }
 9
10 }

car.properties,car的属性文件:

1 name=奥迪A6
2 color=黄色
3 price=50.00

beans.xml,配置文件,细节与之前几篇的栗子不太一样,稍微详说:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans
 6          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 7          http://www.springframework.org/schema/context
 8          http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 9        <context:property-placeholder
10          location="classpath:com/mesopotamia/test2/car.properties"
11          file-encoding="utf8"
12         />
13     <bean id="utf8" class="java.lang.String">
14         <constructor-arg value="utf-8"></constructor-arg>
15     </bean>
16     <bean id="car" class="com.mesopotamia.test2.Car"
17         p:name="${name}"
18         p:color="${color}"
19         p:price="${price}"/>
20 </beans>

运行结果如下:

1 2015-11-22 22:08:59,155  INFO [main] (Car.java:15) - 调用了Car的构造函数,实例化了Car..
2 2015-11-22 22:08:59,167  INFO [main] (Main.java:15) - 名字:奥迪A6 颜色:黄色 价格:50.0

在beans.xml中,

  使用了context规则和p规则。

  <context:property-placeholder下面的两个属性:寻找属性文件,并设定相应的编码方式(默认是ISO-8559-1,加载中文会乱码,所以要转码)

  配置转码属性后要单独写一个13-15行的编码格式的bean(这一点稍微有点淡腾)。

  占位符就如17行那样:${...}。

这样配置后,spring会自动寻找classpath:com/mesopotamia/test2/car.properties下面的相应属性,并以utf-8的格式编码后,赋值给id为car的bean definition的对应属性。

默认情况下,spring会自动启动PropertyPlaceHolderConfigurer这个工厂后处理器。

我们并没有手动注册PropertyPlaceHolderConfigurer,那么spring是如何根据context:property-placeholder就去加载PropertyPlaceHolderConfigurer了?

答案:之前有讲过META-INF文件夹里面的spring-handlers和spring-schemas文件,spring-handlers可以根据命名空间找到具体的处理类,下面是context规则的spring-handlers文件:

1 http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
2
3 http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
4 http
5
6 \://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
7 http
8
9 \://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler

第一行告诉我们,context规则的对应处理类是org.springframework.context.config.ContextNamespaceHandler,我们现在进入这个类:

 1 public class ContextNamespaceHandler extends NamespaceHandlerSupport {
 2
 3     public void init() {
 4         registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
 5         registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
 6         registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
 7         registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
 8         registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
 9         registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
10         registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
11         registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
12     }
13
14 }

第四行一目了然,spring看到property-placeholder,就new了一个PropertyPlaceholderBeanDefinitionParser对象。而这个对象里面有个方法:

1 @Override
2     protected Class getBeanClass(Element element) {
3         return PropertyPlaceholderConfigurer.class;
4     }

这样就清楚了。实际上,<context:property-placeholder下面的属性就是PropertyPlaceholderConfigurer下面的属性,下面是几个重要的属性:

locations:一个属性文件的话,location就够了,而多个属性文件则是locations,它承袭自:

      PropertiesLoaderSupport(PropertyPlaceholderConfigurer的父类的父类)。类型为:Properties[]。

fileEncoding:编码格式。它也是PropertiesLoaderSupport的属性。

order:如果配置文件定义了多个PropertyPlaceholderConfigurer,order负责规定执行顺序。这个属性继承自PropertyResourceConfigurer。

placeholderPrefix:像${...}为例,"${"是占位头符。也可以定义为其他占位头符。比如如果你写成&&{...}&&,那么placeholderPrefix需要定义为&&{。

placeholderSuffix:占位尾符。这两个属性属于PropertyPlaceholderConfigurer。

一般情况下,实例化一个Bean之前是没有对属性进行赋值的,即使实例化的时候也不会进行赋值,这是相当于new了一个对象罢了。但是在配置文件中定义了<bean>的属性值的话,在实例化之前,加载了XML并形成BeanDefinition后,就已经把属性值加载了,这并不是实例化的过程,更不是使用bean对象时定义属性值的过程。

立志不坚,终不济事。

      ------<宋>朱熹

时间: 2024-11-01 18:49:08

(spring-第9回【IoC基础篇】)BeanFactoryPostProcessor,实例化Bean之前的第二大利器的相关文章

(spring-第13回【IoC基础篇】)PropertyEditor(属性编辑器)--实例化Bean的第五大利器

上一篇讲到JavaBeans的属性编辑器,编写自己的属性编辑器,需要继承PropertyEditorSupport,编写自己的BeanInfo,需要继承SimpleBeanInfo,然后在BeanInfo中把特定的属性编辑器和需要编辑的属性绑定起来(详情请查看上一篇). Spring的属性编辑器仅负责将配置文件中的字面值转换成Bean属性的对应值.(而JavaBean的属性编辑器能够通过界面来手动设置bean属性的值).如果属性的类型不同,转换的方法就不同.正如javabean的属性编辑器一样,

(spring-第10回【IoC基础篇】)InstantiationStrategy--实例化Bean的第三大利器

Bean的实例化整个过程如下图: : 其中,BeanDefinition加入到注册表中,并由BeanFactoryPostProcessor的实现类处理后,需要由InstantiationStrategy负责实例化.实例化仅仅是调用构造函数,相当于new了一个对象而已,bean的具体的属性在此时并未赋值(当然,一开始在XML中配置了Bean属性的值,或者在构造函数中有赋值语句的话,相关属性才会在实例化的时候便有了值.).InstantiationStrategy负责由Bean类的默认构造函数.带

(spring-第12回【IoC基础篇】)JavaBean的属性编辑器

在spring实例化bean的最后阶段,spring利用属性编辑器将配置文件中的文本配置值转换为bean属性的对应值,例如: 代码0011 <bean id="car" class="com.mesopotamia.test1.Car" 2 p:name="汽车" 3 p:brand="宝马" 4 p:maxSpeed="200"/> 上面是文本配置, 再看bean: 代码0021 public

(spring-第7回【IoC基础篇】)BeanDefinition的载入与解析&amp;&amp;spring.schemas、spring.handlers的使用

报错信息:Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/p], 一桩事故引发的连锁思考...开幕—— ----------------------------------------------------------------------------------------------

(spring-第14回【IoC基础篇】)国际化信息 (转)

国际化又称为本地化. 当你把手机的language由中文切换到英文时,你的微信也相应改用英语,这就是i18n国际化.一般来说,应用软件提供一套不同语言的资源文件,放到特定目录中,应用根据不同语言的操作系统决定使用哪一种语言. 一般由两个条件限定一个国际化类型:语言类型和国家/地区类型.比如: 中文:语言类型:zh,国家/地区类型:CN(中国大陆)/HK(中国香港)/TW(中国台湾). 英语:语言类型:en,国家类型:EN. ------------------------------------

(spring-第14回【IoC基础篇】)国际化信息

国际化又称为本地化. 当你把手机的language由中文切换到英文时,你的微信也相应改用英语,这就是i18n国际化.一般来说,应用软件提供一套不同语言的资源文件,放到特定目录中,应用根据不同语言的操作系统决定使用哪一种语言. 一般由两个条件限定一个国际化类型:语言类型和国家/地区类型.比如: 中文:语言类型:zh,国家/地区类型:CN(中国大陆)/HK(中国香港)/TW(中国台湾). 英语:语言类型:en,国家类型:EN. ------------------------------------

(spring-第15回【IoC基础篇】)容器事件

五个人在报社订阅了报纸.报社一旦有了新报纸,就派员工分别送到这五个人手里.在这个例子中,“报纸”就是事件,“报社”就是广播器,五个订阅者就是监听器.广播器收到事件,把事件传给监听器,监听器对事件做一些事情.这样的例子属于观察者模式. 观察者模式:A类负责接收新信息,B.C.D类一直关注着A.A类一旦有了新信息,就发送给B.C.D类,B.C.D类收到信息作出不同的操作.这就是观察者模式.具体到代码中,A中设置一个变量message(这就是信息),当message改变时,调用B.C.D的perfor

(spring-第8回【IoC基础篇】)BeanDefinition在IoC容器中的注册

在spring中,所有的bean都是由BeanFactory进行管理的.下面是BeanFactory的类体系结构: 我们清楚的看到,DefaultListableBeanFactory继承了BeanFactory的优良传统,同时又实现了BeanDefinitionRegistry这个注册器,那么无疑,BeanDefinition在容器中的注册任务,非他莫属.事实上,DefaultListableBeanFactory拥有一个私有的BeanDefinitonMap属性,这个属性是个哈希Map,通过

Spring+SpringMVC+MyBatis+easyUI整合基础篇(二)牛刀小试

承接上文,该篇即为项目整合的介绍了. 废话不多说,先把源码和项目地址放上来,重点要写在前面. github地址为ssm-demo 你也可以先体验一下实际效果,点击这里就行啦 账号:admin 密码:123456 从构思这个博客,一直到最终确定以这个项目为切入点,中间也是各种问题出现,毕竟是新人,所以也是十分的小心,修改代码以及搬上github其实花了不少时间,但也特别的认真,不知道是怎么回事,感觉这几天的过程比逼死产品经理还要精彩和享受.或许是博客路上的第一站吧,有压力也有新奇,希望自己能坚持下