现在的位置: 首页 > 综合 > 正文

Spring IoC查缺补漏篇

2013年04月27日 ⁄ 综合 ⁄ 共 7747字 ⁄ 字号 评论关闭
关于资源包的使用
1。basename和basenames
如果你的资源文件只有一个,那么直接使用前者。如果有很多个资源文件,那么就使用后者。
例程代码:
使用basename
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
   <value>spring/p13/messages</value>
</property>
</bean>
使用basenames
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
   <list>
    <value>spring/p13/messages</value>
    <value>spring/p13/msg2</value>
   </list>
</property>
</bean>
2。路径问题
假设你的工程目录是D:/project,你的代码目录是D:/project/src。
如果你的资源放在工程目录的根目录下,那么在使用的时候,可以直接在messageSource配置中的基础文件名中填入文件名即可。
如果你的资源文件放在跟你当前的代码目录相同,假设在D:/project/src/spring/p13,那么在那个basename配置的时候,要这样填写value的值"src/spring/p13/messages",不包含引号。这样MessageSource才能找到正确的资源文件。
3。获取资源档的方式
首先,不要在乎路径分隔符到底是"/"还是"/",JVM会自己搞定。
////////////////////////////////
ApplicationContext的三种实现
////////////////////////////////
ClassPathXmlApplicationContext
当前Classpath的根目录+指定(偏移)路径=资源档案
例如:
当前classpath根目录:D:/project
指定(偏移)路径:src/spring/p14/bean.xml
创建方式:new ClassPathXmlApplicationContext("src/spring/p14/bean.xml");
FileSystemXmllApplicationContext
文件目录(全路径)=资源档案
例如:
创建方式:new FileSystemXmllApplicationContext("d:/project/src/spring/p14/bean.xml");
classpath(相对路径,默认为project的路径)+指定(偏移)路径=资源档案
例如:
当前classpath根目录:D:/project
指定(偏移)路径:src/spring/p14/bean.xml
创建方式:new FileSystemXmllApplicationContext("src/spring/p14/bean.xml");
XmlWebApplicationContext
直接给出相对路径
例如:
你的web应用全称路径:http://www.test.com/prospring
你的资源相对路径:"/WEB-INF/conf/admin.properties"
查找会到:http://www.test.com/prospring/WEB-INF/conf/admin.properties中查找。
注意:相对路径中的开头带不带那个"/",意义完全不同。

带"/"的时候,是从web应用的根目录查找。
对于例子来说根目录就是http://www.test.com/prospring,查找位置是http://www.test.com/prospring/WEB-INF/conf/admin.properties
不带的时候是从当前路径的相对路径查找。
例如,当前路径是http://www.test.com/prospring/test,相对路径是"webimage/admin.properties",那么查找的位置是http://www.test.com/prospring/test/webimage/admin.properties,而不是http://www.test.com/prospring/webimage/admin.properties
此规则同样适用于Web app应用中的所有路径查找。
////////////////////////////////
Resource中协议使用的一点tip
////////////////////////////////
file:协议
记得在学习笔记八的时候,我在末尾曾对原书的file协议中使用了三个"/"提出过质疑。但是经过测试表明,确实能够找到文件。
后来做了n多试验,发现"file:"后面有多少个"/"并不影响文件的正常读取。
例如如下代码:
res = ctx.getResource("file:d:/test.txt");
res = ctx.getResource("file:/d:/test.txt");
res = ctx.getResource("file://d:/test.txt");
res = ctx.getResource("file:///////////////d:/test.txt");
后来干脆去掉盘符,在工程根目录下建立一个测试文件,经测试,相对路径下,无论"/"多少也是一样的成功。
classpath:协议就不说了。
最后说说ServletContextResource,这个在笔记(八)中没有出现。
例程代码:
Resource resource = context.getResource("WEB-INF/conf/admin.properties");
关于它的路径问题,想来也不多废话了,参见上文。
////////////////////////////////
其他的Aware接口
////////////////////////////////
ResourceLoaderAware
还记得这个接口类型把,目的就是让实现它的bean获取ResourceLoader的引用。
ResourceLoader是ApplicationContext所实现的众多接口之一,目的是可以读取资源。但是ApplicationContext“像”(即实现了)ResourceLoader接口,所以在使用上,你会发现与ApplicationContextAware接口无异。
ServletContextAware
同样,是让Bean获取Servlet上下文引用的接口。具体参见Spring文档。
///////////////////////////////////
BeanFactoryPostProcessor接口
///////////////////////////////////
还记得笔记(七)中的CustomEditorConfigurer吧,它是BeanFactoryPostProcessor接口的一个实现类。除此之外,我们还提到了PropertyPlaceholderConfigurerPropertyOverrideConfigurer,这两个都是BeanFactoryPostProcessor接口的实现类。
BeanFactoryPostProcessor接口的定义如下:
public interface BeanFactoryPostProcessor {
     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
如上可知,它可以在BeanFactory完成依赖注入后进行一些后期处理工作。例如变更BeanFactory的配置,使其更具实时性,也因此使得程序变得更具弹性。这里我们将介绍另外两个没有介绍的Configurer。
PropertyPlaceholderConfigurer
作用:使用属性文件,完成依赖注入。
实现:
先看配置文件
<beans>
     <bean id="configBean" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
         <property name="location">
             <value>hello.properties</value>
         </property>
     </bean>
     <bean id="helloBean" class="onlyfun.caterpillar.HelloBean">
         <property name="helloWord">
             <value>${helloWord}</value>
         </property>
         ....
     </bean>
   
      ......
</beans>
假设helloBean中有很多很少变动的属性,但是helloWord这个属性例外,它可能变动的很频繁,怎么办呢?对!对于这样属性可以考虑使用properties文件来解决。
于是configBean中的location属性就指定了属性(properties)文件的文件名——hello.properties,而helloBean中的helloWord属性中则使用EL表达式,其中helloWord对应hello.properties中的消息key。
而hello.properties中就这么一条消息
helloWord=Hello!Justin!
所以,当程序运行时,(调用postProcessBeanFactory()后?),PropertyPlaceholderConfigurer读取属性文件,并且把helloWord对应的值诸如给helloBean。
PropertyOverrideConfigurer
作用:实现对Bean属性值的重载。
实现:
参看配置先
     <bean id="configBean" class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
         <property name="location">
             <value>hello.properties</value>
         </property>
     </bean>
     <bean id="helloBean" class="onlyfun.caterpillar.HelloBean">
         <property name="helloWord">
             <value>Hello!caterpillar!</value>
         </property>
         ....
     </bean>
      ....
这里又出现了属性文件。文件内容如下
helloBean.helloWord=Hello!Justin!
由于使用了PropertyOverrideConfigurer类作为配置类,(调用postProcessBeanFactory()后?),helloWord的值将被覆写掉,所以虽然原来的配置中已经指定它的值是Hello!caterpillar!,但是最终结果会是Hello!Justin!。
用途:特别是在不同权限的问题上时。可以先判定权限,然后决定是否重载属性。这样权限低的用户只能看到Hello!caterpillar!,而高的用户则会看到Hello!Justin!。不失为一个实现代理模式的好方法。
//////////////////////////////////
如何载入所有同类型的bean
//////////////////////////////////
使用BeanFatory的getBeansOfType()方法,该方法返回一个Map类型的实例,Map中的key为Bean的名,key对应的内容为Bean的实例。
该方法有两种类型的重载
getBeansOfType(Class),获取某一类的所有的bean。
getBeansOfType(Class,boolean,boolean),后面两个布尔值,第一代表是否也包含原型(Class祖先)bean或者或者只是singletons(包含FactoryBean生成的),第二个表示是否立即实例化懒加载或者由FactoryBean生成的Bean以保证依赖关系。
例程代码:
配置如下
<bean id="helloBeanOfJustin" class="onlyfun.caterpillar.HelloBean">
     <property name="helloWord">
      <value>Hello!Justin!</value>
     </property>
</bean>
<bean id="helloBeanOfcaterpillar" class="onlyfun.caterpillar.HelloBean">
     <property name="helloWord">
        <value>Hello!caterpillar!</value>
     </property>
</bean>
程序如下
Map helloBeans = factory.getBeansOfType(HelloBean.class, false, false);
//////////////////////////////////
如何加载多个配置文件
//////////////////////////////////
使用XmlBeanDefinitionReader来加载多个配置文件。
它需要使用一个实现了BeanDefinitionRegistry接口的实现类来作为构造参数,例如DefaultListableBeanFactory
例程代码:
BeanDefinitionRegistry reg = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(reg);
   
// 读入bean配置文件   
reader.loadBeanDefinitions(new ClassPathResource("bean1.xml"));
reader.loadBeanDefinitions(new ClassPathResource("bean2.xml"));
....
// 取得Bean
BeanFactory bf = (BeanFactory) reg;
Object o = bf.getBean("helloBean");
因为DefaultListableBeanFactory同时实现了BeanDefinitionRegistry、BeanFactory、ListableBeanFactory等接口,所以应该转换类型后再使用。
//////////////////////////////////
如何使用属性文件配置bean
//////////////////////////////////
实际上,除了xml文件以外,还可以使用属性文件来配置bean,但是好处总是没有xml的多。这里也介绍一下,同时也是为了拓展知识面。
假设某个bean的属性文件,文件全名是:bean.properties,内容如下:
helloBean.class=onlyfun.caterpillar.HelloBean
helloBean.helloWord=Hello!Justin!
则helloBean为bean的id属性,class为bean的class属性。
helloWord是这个bean的实现类中的属性,相当于
<property name="helloWord">
<value>Hello!Justin!</value>
</property>
同时,使用PropertiesBeanDefinitionReader来读取属性文件。
例程代码:
BeanDefinitionRegistry reg = new DefaultListableBeanFactory();
PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(reg);
reader.loadBeanDefinitions(new ClassPathResource("bean.properties"));
BeanFactory factory = (BeanFactory) reg;
//////////////////////////////////
如何在程序中动态配置Bean
//////////////////////////////////
好处:让客户端与配置文件分离
缺陷:硬编码,维护痛苦
对于属性的设置,使用MutablePropertyValues
将属性注入Bean,使用RootBeanDefinition
注册Bean,并生成Bean的别名,使用BeanDefinitionRegistry
最后看看例程代码吧,
// 设置属性
MutablePropertyValues properties = new MutablePropertyValues();
properties.addPropertyValue("helloWord", "Hello!Justin!");

// 设置Bean定义
RootBeanDefinition definition = new RootBeanDefinition(HelloBean.class, properties);
       
// 这册Bean与设定Bean别名
BeanDefinitionRegistry reg = new DefaultListableBeanFactory();
reg.registerBeanDefinition("helloBean", definition);

BeanFactory factory = (BeanFactory) reg;
// 使用
HelloBean hello = (HelloBean) factory.getBean("helloBean");
(IoC部分,完) 

抱歉!评论已关闭.