zoukankan      html  css  js  c++  java
  • (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对象时定义属性值的过程。

    立志不坚,终不济事。

          ------<宋>朱熹

  • 相关阅读:
    学习工具
    Qt 之 QApplication
    Qt中常用的类
    关于在Qt里让程序休眠一段时间的方法总结
    Qt setWindow setViewPort
    ajax回调数据 Structs has detected an unhandled exception 问题
    Struts2配置拦截器自定义栈时抛异常:Unable to load configuration.
    es6之map解构数组去重
    ES6之对象的方法
    ES6之genorator和yield使用(迭代器)
  • 原文地址:https://www.cnblogs.com/mesopotamia/p/4987092.html
Copyright © 2011-2022 走看看