今天办理交接手续,没有安排任务,继续整理剩下的多种Bean注入的文章,如果还有时间谈谈我理解的IoC原理方面的东东和Bean的生命周期,一步步打通任督二脉。
正如上篇所讲,Spring是支持各种对象依赖注入的,注入的方式也灵活多变,下面以Setter为主,讲解我所了解的一些类型的注入:
1.基本类型
先看下面的配置,Java部分省略(例子全部来源于网络):
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" init-method="init" destroy-method="close"> <property name="username"> <value>root</value> </property> <property name="password"> <value>masterkaoli</value> </property> </bean>
从配置来看,总共注入了两个String类型的变量值,username、password,如果注入的是integer或别的类型,触类旁通。重点了解init-method和destroy-method两个属性,前者可配置一个初始化方法,用来完成构造器以后执行的方法,一些需要类中需要初始化的事情可以都交给他来做;destroy-method这个方法一般是在容器销毁的时候调用,采用此种配置,Spring容器将使用JavaBeanPropertyEditor把<value/>元素中的字符转换为实际属性或参数类型,Spring团队较倾向于此种方式。
为尽可能全面些,提供另一种配置方式:
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" init-method="init" destroy-method="close"> <property name="username" value="root"/> <property name="password" value="masterkaoli"/> </bean>
除了<property>,<constructor-arg/>也支持“value”属性,这样会使配置比较简单,本人较喜欢这种配置方式。
再看一个例子:
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean" /> </property> </bean>
这个配置等同于下面的配置:
<bean id="theTargetBean" class="..." /> <bean id="client" class="..."> <property name="targetName" value="theTargetBean" /> </bean>
上面的配置中没有出现<idref/>这个元素,细心的读者可能已经发现了,<idref/>其实就是“value”,不同是什么呢?<idref/>比value多了一个验证功能,他会在容器部署的时候去验证是否真的存在一个叫“theTargetBean”的Bean,如果没有则会抛出异常,如此就能降低后期错误的几率,因此,推荐使用<idref/>,它与<ref/>的区别后面我会讲到。
2.引用其他Bean
引用外部的Bean一般靠<ref/>元素,举例说明:
<bean name="/errorHandleConfigAction" class="com.action.ErrorHandleConfigAction"> <property name="errorStrategyService"> <ref bean="errorStrategyService"/> </property> </bean>
这种形式是较为常见的形式,通过<ref/>标签的bean属性来指定目标BeanerrorStrategyService,此种配置方式会引用同一容器中内的任何Bean,无论是否是在同一xml中。到这里你可能也就明白了<ref/>和<idref/>的本质区别了:前者引用的是bean的一个实例对象,而后者引用的仅仅是bean的id或name,这两个标签的区别不是一般的大,是不能搞混的。
再看一个例子:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local=" sessionFactory"/> </property> </bean>
这种形式是通过<ref/>标签的local属性指定目标bean,他利用XML解析器来验证引用bean是否在同一文件中,如果在同一xml中没有找到,则会抛出异常,local属性值必须是目标bean的id属性值,在同一个文件中,推荐使用local。
上面两个例子中,我在第一个bean的定义中用了name,而第二个用了id,他俩有什么区别呢?首先说,id和name同样都是指定bean的名称,但使用场景有别;id是bean的唯一标识,不能重复,XML解析器会根据DTD来验证被引用的有效性,但这也带来了id命名规则方面的限制,不能以数字符合打头,不能出现空格,因此,在Spring和Struts整合中要格外注意了。
基于这个缺陷,name应运而生,他支持重名,一般是后面的覆盖前面的,支持一bean多名,中间可用逗号分隔,如:<beanname="b1,b2,b3">;一般如果id属性不存在,则name中的第一个充当id。所以,一般情况下,推荐使用id属性!
还有一种形式的引用是,通过<ref/>标签中的parent属性引用当前容器中父容器的bean,对此不甚了解,感兴趣的可以百度下。
3.内联和外联
本想一气呵成,可内容貌似有点多,分成上中下三篇吧,这篇结尾说说项目中常见的两种配置风格。根据应用场景,把bean之间的依赖注入的配置划分成内联和外联,我自己这么叫,不知准确否?外联就是我上面第2条这种形式;那何为内联呢,也叫内部bean,举例说明:
<bean id="outer" class="..."> <!-- instead of using a referenceto a target bean, simply define the target bean inline --> <propertyname="target"> <bean class="com.example.Person"> <!-- this is the inner bean --> <property name="name" value="Fiona Apple"/> <property name="age" value="25"/> </bean> </property> </bean>
从上面的例子看出,outer依赖target这个bean,但他并没有采用我们在第2条引用其他bean所说的那样,采用<ref/>标签去处理,而是直接使用<bean/>标签指向了实际的目标bean,内部bean总是匿名并且他们总是prototype模式,同时将内部bean注入到包含该内部bean之外的bean是不可能的,内部bean定义不需要指定id或name属性,即使指定也会被容器所忽略,虽然这样让配置清爽了不少,但也是缺陷,就是不能复用。在没有复用的情况下推荐使用内联;而需要复用时,推荐使用外联(上面第2条已经讲过了,不再举例)。