所谓装配(wiring),就是将组件状态到Spring容器中,并且在这些组件建立联系。
可以使用Xml文件,properties文件,数据库,甚至LDAP目录进行装配,但Xml文件是最常用的装配方式,我们这里就介绍以下基于Xml文件的装配
(一)大致框架
bean的装配很简单,使用<beans>作为根节点,使用<bean>将对应bean放Spring容器中,例如
<beans>
<bean id="hello1" class="com.company.yingyong.xxx.Hello1Class"></bean>
<bean id="hello2" class="com.company.yingyong.xxx.Hello2Class"></bean>
</beans>
其中class属性里面是一个类的全路径
这样就有了beans的简单框架
(二)prototype vs singleton
Singleton就是单例形式,每次getBean产生的对象都是同一个对象;而prototype类型的bean,只是定义了一个蓝图(blueprint),每次getBean都是获取新的对象。在默认情况下,bean都是singleton的,如果要设置,则使用bean的属性singleton来进行设置,例如:
<bean id="x1" class="" singleton="true"></bean>(默认情况)
<bean id="x2" class="" singleton="false"></bean>(prototype的bean)
prototype的bean,当它与数据库或者网络连接等有限资源相关时,就会造成性能等损失,应该慎重使用
而在合适的情况下,prototype的bean,使用起来很像工厂,每次都会产生新的对象。
(三)初始化与消亡
Spring可以定义bean初始化和消亡时调用的方法,有两种方式来指定
方法一是使用bean的属性:
<bean init-method="init" destroy-method="destroy"...>...
init和destroy是初始化和消亡是会被调用的方法名
方法二是实现接口:
初始化:如果实现了InitializingBean,那么在属性被set之前beforePropertiesSet()方法会被调用,那么只要实现这个接口并实现这个方法就可以了。
消亡:相应的接口是DisposableBean,方法为destroy()
使用方法二的话,Spring会自动获取初始化和消亡时调用的方法,不用配置,但另一方面,这两个接口都是Spring的API,如果使用方法二,那么自己的类将会与Spring的API捆绑,这似乎不太合理。所以,当开发一个与Spring框架相关的bean时可以考虑使用方法二。
(四)setter装载
使用<property>节点来注入属性,可以注入任何类型的属性,下面分几种类型来分别介绍
[1]Java基本类型或者String:使用value
<bean id="hello1" class="...">
<property name="name">
<value>minna_han</value>
</property>
</bean>
[2]其他bean:使用ref
<bean id="hello1" class="...">
<property name="name">
<ref bean="anotherBean"/>
</property>
</bean>
[3]内部bean:使用bean
<bean id="hello1" class="...">
<property name="name">
<bean id="innerBean" class="..xxx..."></bean>
</property>
</bean>
[4]Collections:
(1)数组和List(java.util.List):使用list
<bean id="hello1" class="...">
<property name="nameList">
<list>
<value>name1</value>
<value>name2</value>
<ref bean="anotherBean"/>
<bean id="id1" class="...xxx..."></bean>
</list>
</property>
</bean>
list里面可以使用value,ref,bean,甚至是其他list,和其他Collections类型,null
(2)Set(java.util.Set):与(1)数组和List(java.util.List)类似
(3)Map(java.util.Map):值得注意的是,Spring只支持key值为String类型的Map(java中的Map的Key值可以为任何类型)
<bean id="hello1" class="...">
<property name="nameList">
<map>
<entry key="key1">
<value>name1</value>
</entry>
<entry key="key2">
<ref bean="anotherBean"/>
</entry>
<entry key="key2">
<map>
....
</map>
</entry>
</map>
</property>
</bean>
entry里面对应的值,可以用value,ref,bean,甚至是其他map,和其他Collections类型,null
(4)Properties(java.util.Properties)
<bean id="hello1" class="...">
<property name="nameList">
<props>
<prop key="x1">xx1</prop>
<prop key="x2">xx2</prop>
...
</props>
</property>
</bean>
其中,xx1,xx2必须是String,这点与Properties一致
[5]null
如果你不去配置某一个属性值,那么它会被置为默认值,或者因autowiring被自动装载;而这时你真的需要这个属性值确定为null值,那么你可以使用<null/>
(五)constructor装载
与setter装载类似,使用<constructor-arg>,而不是<property>
<bean id="hello1" class="...">
<constructor-arg index="2">
...abc...
</constructor-arg>
<constructor-arg type="java.lang.String">
...abc...
</constructor-arg>
</bean>
constructor-arg使用index或者type方式来决定对应的参数,所以,如果两个参数的类型都是String,那么就会抛出UnsatisfiedDependencyException,因为无法将参数和参数值匹配,而使用index则可以明确匹配
constructor-arg中间的内容可以使用前面setter装载<property>里面的所有方式(在参数匹配的情况下),进行装载。
(六)setter vs constructor装载
constructor比setter来说,更稳定和明确地设置依赖,setter方式,在bean被初始化时,bean的属性可能未被设置或者可能为一些非合法值;某些属性,是特定的,你希望它只被设置依次,而且不改变它,那么可以用constructor方式来进行注入依赖。
setter和constructor装载没有什么伯仲之分,它们只是实现装载的两种方法,在实际应用中,可以根据需求混合使用