内容参考自spring in action一书。
创建应用对象之间协作关系的行为通常称为装配,这也是依赖注入的本质。
1. 创建spring配置
spring是一个基于容器的框架。如果没有配置spring,那么它就是一个空的容器,所以需要配置spring来告诉容器它需要加载哪些Bean和如何装配这些bean,这样才能确保它们能够彼此协作。
从spring3.0开始,spring容器提供了两种配置bean的方式。第一种是传统上的使用一个或多个XML文件作为配置文件。第二种提供了基于java注解的配置方式。
先说XML文件的配置方式:
在XML文件中声明Bean时,Spring配置文件的根元素是来源于Spring Beans命名空间所定义的<beans>元素。下面是一个典型的Spring XML配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Bean declarations go here -->
</beans>
在<beans>元素内可以放置所有的Spring配置信息,包括<bean>元素的声明。但是beans的命名空间并不是唯一的Spring命名空间。Spring的核心框架自带了10个命名空间配置。如下所示:
2. 注入bean属性
spring注入bean属性有三种方式,常用的有两种,第一种是通过构造器注入。第二种是通过Setter注入。
第一种,通过构造器注入:
先看最简单的构造器注入方式,通过默认构造函数进行注入。
<bean id="duke" class="com.pcitc.springInAction.Juggler" />
2.1.1 通过构造器注入简单值:
现在进行按需实例化,不走默认的构造方法:
<bean id="duke" class="com.pcitc.springInAction.Juggler">
<constructor-arg value="15" />
</bean>
总结:在构造bean的时候可以使用 <constructor-arg>元素来告诉spring额外的信息。如果不配置 <constructor-arg>元素,那么spring将使用默认的构造方法,上面配置了 <constructor-arg>的value属性设置为15,Spring将调用Juggler的另一个构造方法。
2.1.2 通过构造器注入对象引用:
<bean id="sonnet29" class="com.pcitc.springInAction.Sonnet29" />
<bean id="poeticDuke" class="com.pcitc.springInAction.PoeticJuggler">
<constructor-arg value="15" />
<constructor-arg ref="sonnet29" />
</bean>
总结: <constructor-arg ref="sonnet29" />,把bean引用传递给构造器,当spring碰到sonnet29和duke的声明时,它所执行的逻辑本质上和下面的java代码相同:
Poem sonnet29 = new Sonnet29();
Performer duke = new PoeticJuggler(15, sonnet29);
通过工厂方法创建Bean:
假如想声明的Bean没有一个公开的构造方法,那怎么办呢?方法是通过工厂方法创建bean。
有时候静态工厂方法是实例化对象的唯一方法。Spring支持通过<bean>元素的factory-method属性来装配工厂创建的bean。
定义一个单例类:
package com.pcitc.springInAction; public class Stage {
private Stage() {
} private static class StageSingletonHolder {
static Stage instance = new Stage();
} public static Stage getInstance() {
return StageSingletonHolder.instance;
}
}
Stage类没有一个公开的构造方法,这时候只能通过Spring的factory-method属性调用一个指定的静态方法,从而代替构造方法来创建一个类的实例。
在Spring配置文件下这样来声明该bean
<bean id="theStage" class="com.pcitc.springInAction.Stage"
factory-method="getInstance" />
第二种,通过Setter方法注入:
通常javabean的属性都是私有的,同时拥有一组存取器方法,以setXXX和getXXX形式存在.spring可以借助属性的set方法来配置属性的值,以实现setter方法注入。
简单说明:
2.2.1 注入简单值的方式:
<bean id="kenny"
class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
</bean>
在spring配置文件中,一旦Instrumentalist被实例化,<peoperty>元素会指示Spring调用setSong方法将song属性的值设置为"Jingle Bells"。需要说明的是value值的类型不基于String类型,也可以是数值类型和boolean类型,spring会根据属性值的类型进行自动转换。
2.2.2 通过Setter注入对象引用:
<bean id="kenny2"
class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="saxophone" />
</bean>
注入内部bean:
<bean id="kenny"
class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
<property name="instrument">
<bean class="org.springinaction.springidol.Saxophone" />
</property>
</bean>
内部bean是通过直接声明一个<bean>元素作为<property>元素的子节点而定义的,内部bean并限于setter注入,还可以把内部bean装配到构造方法的入参中,如下所示:
<bean id="duke"
class="com.springinaction.springidol.PoeticJuggler">
<constructor-arg value="15" />
<constructor-arg>
<bean class="com.springinaction.springidol.Sonnet29" />
</constructor-arg>
</bean>
在这里,Sonnet29的一个实例将作为内部bean被创建,并作为参数传递给PoeticJuggler的构造器。
总结:需要注意的是内部Bean没有ID属性。虽然为内部bean配置一个ID属性是合法的,但是完全没有必要,因为永远不会通过名字来引用内部bean。这也突出了使用内部bean的最大缺点:它们不能被复用。内部bean仅适用于一次注入,而且也不能被其他bean所引用,而且某种程度上还会影响Spring XML配置的可读性。
3. 使用Spring的命名空间p装配属性
Spring的命名空间p提供了另外一种Bean属性的装配方式,命名空间的schema URI为
http://www.springframework.org/schema/p.如果想使用命名空间p,只需要在Spring的XML配置中增加如下一段声明:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
使用方式如下:
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
p:song = "Jingle Bells"
p:instrument-ref = "saxophone" />
p:song属性的值被设置为"Jingle Bells",将使用该值装配bean属性。p:instrument-ref属性的值被设置为"saxophone",将使用一个ID为saxophone的Bean引用来装配instrument属性。-ref后缀作为一个标识来告诉Spring应该装配一个引用而不是字面值。
4. 装配集合元素
上面Spring的几种配置方式都只能配置简单属性值和引用其他bean的属性,当bean的属性值的类型是集合时只能使用集合的配置方式,Spring提供了四种类型的集合配置方式:
当装配类型为数值或者java.util.Collection任意实现的属性时,<List>和<Set>元素都可以用,其实属性实际定义的集合类型与选择<List>和<Set>没有任何关系。如果属性为任意的java.util.Collection类型时,这两个配置元素在使用时安全可以互换,不能因为属性为java.util.Set类型,就表示必须使用<set>元素来完成装配。使用<set>元素配置java.util.List类型的属性是可以的,只需要确保List中的每一个成员都必须是唯一的。
<map>和<props>这两个元素分别对应java.util.Map和java.util.Properties。当需要由键-值对组成的集合时,这两种配置元素非常有用,这两种配置元素的关键区别在于,<props>要求键和值都必须为String类型,而<map>允许键和值可以是任意类型。
4.1 装配集合元素之装配List,Set和Array
Java对instruments的声明如下:
private Collection<Instrument> instruments;
spring的配置文件配置如下:
<bean id="hank"
class="com.springinaction.springidol.OneManBand">
<property name="instruments">
<list>
<ref bean="guitar" />
<ref bean="cymbal" />
<ref bean="harmonica" />
</list>
</property>
</bean>
<list>元素包含一个或多个值。<ref>元素用来定义Spring上下文中的其他bean的引用。当然还可以使用其他的Spring设置元素作为<list>的成员,包括<value>,<bean>和<null/>,实际上<list>还可以包含另外一个<list>作为其成员,形成多维列表。
即使像下面的方式来配置instruments属性,<list>元素也一样有效:
java.util.List<Instrument> instruments;或者Instrument[] instruments;
同样有可以使用<set>元素来装配集合类型或者数组类型的属性:
<bean id="hank"
class="com.springinaction.springidol.OneManBand">
<property name="instruments">
<set>
<ref bean="guitar" />
<ref bean="cymbal" />
<ref bean="harmonica" />
<ref bean="harmonica" />
</set>
</property>
</bean>
4.2 装配集合元素之装配Map集合
Java对instruments的声明如下:
private Map<String, Instrument> instruments;
spring的配置文件配置如下:
<bean id="hank" class="com.springinaction.springidol.OneManBand">
<property name="instruments">
<map>
<entry key="GUITAR" value-ref="guitar" />
<entry key="CYMBAL" value-ref="cymbal" />
<entry key="HARMONICA" value-ref="harmonica" />
</map>
</property>
</bean>
<map>元素声明了一个java.util.Map类型的值。每个<entry>元素定义Map的一个成员。key元素指定了entry的键,而value-ref属性定义了entry的值,并引用了spring上下文中的其他bean。<entry>元素还有两个属性分别可以用来指定entry的键和值。
当键和值都不是String类型时,将键-值对注入到Bean属性的唯一方法就是使用<map>元素。
4.3 装配集合元素之装配Properties集合
如果所配置的Map的每一个entry的键和值都为String类型时,可以考虑使用java.util.Properties代替Map。Properties类提供了和Map大致相同的功能,但是它限定键和值必须是String类型。
Java对instruments的声明如下:
private Properties instruments;
spring的配置文件配置如下:
<bean id="hank" class="com.springinaction.springidol.OneManBand">
<property name="instruments">
<props>
<prop key="GUITAR">STRUM STRUM STRUM</prop>
<prop key="CYMBAL">CRASH CRASH CRASH</prop>
<prop key="HARMONICA">HUM HUM HUM</prop>
</props>
</property>
</bean>
Properties集合的配置要点:<property>元素用于把值或Bean引用注入到Bean的属性中;<props>元素用于定义一个java.util.Properties类型的集合值;<prop>元素用于定义<props>集合的一个成员。
4.4 装配空值
如果需要把属性值设置为null值,那么可以显示的为该属性装配一个null值。显示的为属性装配null值将会覆盖自动装配的值。
<property name="someNonNullProperty"><null/></property>