在 Spring 配罝文件中,用户不但可以将 String、int 等字面值注入 Bean 中,还可以将集合、Map 等类型的数据注入 Bean 中,此外还可以注入配置文件中其他定义的 Bean。
1.字面值
所谓 "字面值" 一般是指可用字符串表示的值,这些值可以通过 <value> 元素标签进行注入,在默认情况下,基本数据类型及其封装类、String 等类型都可以采取字面值注入的方式。Spring 容器在内部为字面值提供了编辑器。它可以将以字符串表示的字面值转换为内部变量的相应类型。Spring 允许用户注册自定义的编辑器,以处理其他类型属性注入时的转换工作。
在下面的示例中,我们为 Car 注入了两个属性值,并在 Spring 配罝文件中使用字面值提供配置值,如下所示。
字面值注入字面值
<bean id="car" class="com.smart.attr.Car" lazy-init="default"> <property name="brand"> <value><![CDATA[红旗&CA72]]></value> </property> <property name="maxSpeed">① <value>200</value> </property> <property name="price" value="2000.00" /> </bean>
由于①处的 brand 属性值包含一个 XML 的特殊符号,因此我们特意在属性值外添加了一个XML特殊标签 <![CDATA[]]>。<![CDATA[]]> 的作用是让 XML 解析器将标签中的字符串当作普通的文本对待,以防止特殊字符串对 XML 格式造成破坏。
XML中共有5个特殊的字符,分别是 &、<、>、“、' 。如果配置文件中的注入值包括这些特殊字符,就需要进行特别处理。有两种解决方法:其一,采用本例中的 <![CDATA[]]> 特殊标签,将包含特殊字符的字符串封装起来;其二,使用 XML 转义序列表示这些特殊字符,这5个特殊字符所对应的XML转义序列特殊符号
特殊符号 转义序列
< <
> >
& &
“ "
' '
如果使用XML转义序列,则可以使用以下配置替换上面代码的配置。
<property name="brand"> <value>红旗&CA72</va1ue> </property>
注意:一般情况下,XML 解析器会忽略元素标签内部字符串的前后空格,但 Spring 却不会忽略元素标签内部字符串的前后空格如通过以下配置为 brand 属性提供注入值:
<property name="brand"><value> 红旗CT72 </value></property>
那么 Spring 会将 "红旗CT72" 连同其前后空格一起赋给 brand 属性
2.引用其他 Bean
Spring IOC 容器中定义的 Bean 可以相互引用,IOC 容器则充当 "红娘" 的角色。下面创建一个新的 Boss 类,Boss 类中拥有一个 car 类型的属性。
public class Boss { private Car car; //①设置 car 属性 public void setCar(Car car) { this.car = car; } ... }
boss 的 Bean 通过 <ref> 元素引用 car Bean,建立起 boss 对 car 的依赖。
<bean id="car" class="com.smart.attr.Car"/> <bean id="boss" class="com.smart.attr.Boss"> <property name="car"> <ref bean="car" /> </property> </bean>
<ref> 元素可以通过以下3个属性引用容器中的其他Bean
1、bean:通过该属性可以引用同一容器或父容器中的 Bean,这是最常见的形式。
2、local:通过该属性只能引用同一配置文件中定义的 Bean,它可以利用 XML 解析器自动检验引用的合法性,以便开发人员在编写配置时能够及时发现并纠正配置错误。
3、parent:引用父容器中的Bean,如 <ref parent="car"> 的配置说明 car 的 Bean 是父容器中的 Bean。
为了说明子容器对父容器中 Bean 的引用,我们来看一个具体的例子。假设有两个配置文件 beans1.xml 和 beans2.xml,其中 beans1.xml 被父容器加载,其配置内容如下:
<bean id="car" class="com.smart.attr.Car"> <property name="brand" value="红旗CA72" /> <property name="maxSpeed" value="200" /> <property name="price" value="2000.00" /> </bean>
而 beans2.xml 被子容器加载,其配置内容如下:
<bean id="car" class="com.smart.attr.Car"> <property name="brand" value="吉利CT5" /> <property name="maxSpeed" value="100" /> <property name="price" value="1000.00" /> </bean> <bean id="boss" class="com.smart.attr.Boss"> <property name="car"> <ref parent="car" /> </property> </bean>
在 beans1.xml 中配置了一个 car Bean,在bean2.xml中也配置了一个 car Bean。分别通过父、子容器加载 beans1.xml 和 beans2.xml,beans2.xml 中的 boss 通过<ref parent="car">引用父容器中的car。
下面是分别使用父、子容器加载 beans1.xml 和 beans2.xml 配置文件的代码:
//①父容器 ClassPathXmlApplicationContext pFactory = new ClassPathXmlApplicationContext(new String[]{"com/smart/attr/beans1.xml"}); //②指定 pFactory 为该容器的父容器 ApplicationContext factory = new ClassPathXmlApplicationContext(new String[]{"com/smart/attr/beans2.xml"},pFactory); Boss boss = (Boss)factory.getBean("boss"); assertNotNull(boss); System.out.println(boss.getCar().toString());
运行这段代码,在控制台中打印出以下信息:
brand:红旗CA72/maxSpeed:200/price:2000.0
3.内部Bean
如果 car Bean 只被 boss Bean引用,而不被容器中任何其他的 Bean 引用,则可以将 car 以内部 Bean 的方式注入 Boss 中。
<bean id="boss" class="com.smart.attr.Boss"> <property name="car"> <bean class="com.smart.attr.Car"> <property name="maxSpeed" value="200"/> <property name="price" value="2000,00"/> </bean> </property> </bean>
内部 Bean 和 Java 的匿名内部类相似,既没有名字,也不能被其他 Bean 引用,只能在声明处为外部 Bean 提供实例注入。
内部 Bean 即使提供了 id、name、scope 属性,也会被忽略。
4.null 值
如果用户尝试通过以下配置方式为 car 的 brand 属性注入一个 null 值,那么将会得到一个失望的结果。
<bean id="car" class="com.smart.attr.Car"> <property name="brand"><value></value></property> </bean>
Spring 会将 <value></value> 解析为空字符串。那么,如何为属性设置一个 null 的注入值呢?答案是必须使用专用的 <null/> 元素标签,通过它可以为 Bean 的字符串或其他
对象类型的属性注入 null 值。
<property name="brand"><null/></property>
上面的配置代码等同于调用 car.setBrand(null) 方法。
5.级联属性
和 Struts、Hibernate 等框架一样,Spring 支持级联属性的配置。假设我们希望在定义 Boss 时直接为 car 的属性提供注入值,则可以采取以下配置方式:
<bean id="boss3" class="com.smart.attr.Boss"> <!-- 以圆点(.)的方式定义级别属性--> <property name="car.brand" value="吉利CT50"/> </bean>
按照上面的配置,Sping 将调用 Boss.getCar().setBrand("吉利CT50")方法进行属性的注入操作。这时必须对 Boss 类进行改造,为 car 属性声明一个初始化对象。
public class Boss { //声明初始化对象 private Car car = new Car(); public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } ... }
在①处为 Boss 的 car 属性提供了一个非空的 Car 实例。如果没有为 car 属性提供 Car 对象,那么 Spring 在设置级联属性时将抛出 NullValueInNestedPathExcepüon 异常。
Spring 没有对级联属性的层级数进行限制,只要配置的 Bean 拥有对应于级联属性的类结构,就可以配置任意层级的级联属性,如以下定义了具有三级结构的级联属性。
<property name="car.wheel.brand" value="双星"/>
6.集合类型属性
java.util 包中的集合类型是最常用的数据结构类型,主要包括 List、set、Map、Properties,Spring 为这些集合类型属性提供了专属的配置标签。
1)List
为 Boss 添加一个 List 类型的 favorites 属性,如下:
public class Boss { private List favorites = new ArrayList(); public List getFavorites() { return favorites; } public void setFavorites(List favorites) { this.favorites = favorites; } }
对应 Spring 中的配置片段如下:
<bean id="boss1" class="com.smart.attr.Boss"> <property name="favorites"> <list> <value>看报</value> <value>赛车</value> <value>高尔夫</value> </list> </property> </bean>
List 属性既可以通过 <value> 注入字符串,也可以通过 <ref> 注入容器中其他的Bean。
注意:假设一个属性类型可以通过字符串字面值进行配置,那么该类型对应的数组类型的属性(如String[]、int[] 等)也可以采用 <list> 方式进行配置。
2)Set
如果 Boss 的 favorites 属性是 java.util.set,则采用如下配置方式:
<bean id="boss1" class="com.smart.attr.Boss"> <property name="favorites"> <set> <value>看报</value> <value>赛车</value> <value>高尔夫</value> </set> </property> </bean>
3)Map
下面为 Boss 添加一个 Map 类型的 jobs 属性:
public class Boss { ... private Map jobs = new HashMap(); public Map getJobs() { return jobs; } public void setJobs(Map jobs) { this.jobs = jobs; } ... }
在配置文件中可以通过以下方式为 jobs 属性提供配置值:
<bean id="boss1" class="com.smart.attr.Boss"> <property name="jobs"> <map> <entry > <key><value>AM</value></key> <value>会见客户</value> </entry> <entry> <key><value>PM</value></key> <value>公司内部会议</value> </entry> </map> </property> <bean>
假如某一 Map 元素的键和值都是对象,则可以采取以下配置方式:
<entry> <key><ref bean="keyBean"/></key> <ref bean="vaIueBean"/> </entry>
4)Properties
Properties 类型其实可以看作 Map 类型的特例。Map 元素的键和值可以是任何类型的对象,而 Properties 属性的键和值都只能是字符串。下面为 Boss 添加一个 Properties 类型的 mails 属性:
public class Boss { ... private Properties mails = new Properties(); public Properties getMails() { return mails; } public void setMails(Properties mails) { this.mails = mails; } ... }
下面的配置片段为 mails 提供了配置:
<bean id="boss1" class="com.smart.attr.Boss"> <property name="mails"> <props> <prop key="jobMail">john-office@smart.com</prop> <prop key="lifeMail">john-life@smart.com</prop> </props> </property> </bean>
因为 Properties 键值对只能是字符串,因此其配置比 Map 的配置要简单一些,注意值的配置没有 <value> 子元素标签。
5)强类型集合
Java5.0 提供了强类型集合的新功能,允许为集合元素指定类型。如下面 Boss 类中的 jobTime 属性就采用了强类型的 Map 类型,元素的键为 String 类型,而值为 Integer类型。
public class Boss { ... private Map<String, Integer> jobTime = new HashMap<String, Integer>(); public Map<String, Integer> getJobTime() { return jobTime; } public void setJobTime(Map<String, Integer> jobTime) { this.jobTime = jobTime; } ... }
在 Spring 中的配置和非强类型集合相同
<bean id="boss1" class="com.smart.attr.Boss"> <property name="jobTime"> <map> <entry> <key><value>会见客户</value></key> <value>124</value>//① </entry> </map> </property> </bean>
但 Spring 容器在注入强类型集合时会判断元素的类型,将设置值转换为对应的数据类型。如①处的设置项 124 将被转换为 Integer 类型。
6)集合合并
Spring支持集合合并的功能,允许子 <bean> 继承父 <bean> 的同名属性集合元素,并将子 <bean> 中配置的集合属性值和父 <bean> 中配置的同名属性值合并起来作为最终 Bean 的属性值,如下
<bean id="parentBoss" abstract="true" class="com.smart.attr.Boss"> <property name="favorites"> <!--①父<Bean> --> <set> <value>看报</value> <value>赛车</value> <value>高尔夫</value> </set> </property> </bean> <bean id="childBoss" parent="parentBoss"> <!--②指定父<Bean> --> <property name="favorites"> <set merge="true"> <!--③和父<Bean>中同名集合属性合并 --> <value>爬山</value> <value>游泳</value> </set> </property> </bean>
③处通过 merge="true" 属性指示子 <bean> 和父 <bean> 中的同名属性值进行合并,即子 Bean 的 favorites 集合最终将拥有5个元素。如果设置为 merge="false",则不会和父 <bean> 中的同名集合属性进行合并,即子 Bean 的 favorites 属性集合只有两个元素
7)通过 util 命名空间配置集合类型的 Bean
如果希望配置一个集合类型的 Bean,而非一个集合类型的属性,则可以通过 util 命名空间进行配置。首先需要在 Spring 配置文件头中引入命名空间的声明。
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> ... </beans>
其次配置一个List类型的Bean,可以通过list-class显式指定List的实现类。
<util:list id="favoriteList1" list-class="java.util.LinkedList"> <value>看报</value> <value>赛车</value> <value>高尔夫</value> </util:list>
再次配置一个 Set 类型的 Bean,可以通过 set-class 指定 Set 的实现类。
<util:set id="favoriteSet1" > <value>看报</value> <value>赛车</value> <value>高尔夫</value> </util:set>
最后配置一个 Map 类型的Bean,可以通过 map-class 指定 set 的实现类。
<util:map id="emails1" > <entry key="AM" value="会见客户" /> <entry key="PM" value="公司内部会议" /> </util:map>
此外,<util:list> 和 <util:set> 支持 value-type 属性,指定集合中的值类型;而 <util:map> 支持 key-type 和 value-type 属性,指定 Map 的键和值类型。