本文将会介绍Spring框架中wiring beans的相关内容,包括:bean的声明与创建、在xml文件中对bean基本使用方法的配置以及Spring 3中新特性Spring Expression Language(即SpEL)的初步介绍。

在引出bean之前,不得不提到container。Container用于描述包含并维护其他组件(component)的组件。在Spring框架中,提供了两种Container,即BeanFactory和ApplicationContext。其中,BeanFactory是最简单的一种Container,能够提供基本的依赖注入(DI)和wring bean的服务。而当需要使用到更加高级的Spring框架服务,例如Spring AOP、事件传递及各种不同应用层的context实现等时,则ApplicationContext能够显示出其优势。而提及bean,则可以简单地理解为是经过实例化,编译的对象。它是由container进行管理的。

下面开始详细介绍bean在Spring中的配置。

以下代码是一个典型的Spring中beans的xml配置。其中bean的声明处于<beans></beans>之中。当然,除了beans这个命名空间外,还有例如aop,context,jee,util等更加高级和复杂的命名空间存在,他们和<beans>是同级别的。本文中只关注<beans>命名空间。例如现在存在一个类,它的定义如下:

  1. package org.Spring.demo;  
  2. public class Movie {  
  3.   private movieName; 
  4.   private introInfo
  5.   public Movie() {  
  6.   }  
  7.   public Movie(String movieName, String introInfo) {  
  8.     this.movieName = movieName; 
  9.     this.introInfo = introInfo
  10.   } 
  11.   public void display() { 
  12.     system.out.println(movieName + ":" + introInfo); 
  13.   } 

以下是电影TheStarWar作为Movie的一个实例而被定义为一个Spring bean:

  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <beans xmlns="http://springframework.org/schmema/beans"  
  3.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.         xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.   <bean id="StarWars" class="com.Spring.demo.Movie" > 
  7.     <constructor-arg value="Star Wars" /> 
  8.     <constructor-arg value="bla bla bla bla" /> 
  9.   </bean>  
  10. </beans>  

其中,class="com.Spring.demo.Movie"和Java代码进行关联,从而根据xml文件中的值进行Movie的实例化,最终结果得到名为Star Wars的实例StarWars。此时,我们便可以使用ApplicationContext对象来使用已经配置过的xml:

  1. ApplicationContext ctx = new ClassPathXmlApplicationContext(  
  2.         "com/Spring/demo/spring-demo.xml");  
  3. Movie starWars = ctx.getBean("StarWars"); 
  4. starWars.display(); 

另外情况是,Movie的构造函数的一个参数另外一个对象,例如,Movie对象的构造函数多了一个Music类型的参数(背景音乐的需要),则需在xml配置中增加一个构造函数参数:

  1. <constructor-arg ref="ANewHopeandEndCredits"> 

属性'ref'代表了一种引用关系。它是另外一个类型为Music,并已于beans内定义的实例。此处,Music类的详细定义暂且略去。

除此之外,我们知道bean是有一个生命周期的。Spring为其提供了两个beans的属性:default-init-method, default-destory-method。它们分别对应类的某个特定方法。

我们知道,在很多时候一个类里面存在setters和getters方法。例如,上述例子中,引入了Music类型的字段之后,可能出现情形:

  1. private Music music ; 
  2.   public void setMusic(Music music) { 
  3.     this.music = music; 
  4.   public Music getMusic() { 
  5.     return music; 

针对这种情况,Spring提供了与construct-arg平行的标签:<property>。例如:

  1. <property name="music" ref="ANewHopeandEndCredits" /> 

当然,此处的ref表示的是引用类型,该类型一再beans中定义。如果是值类型,则可以直接使用value属性。

Spring还提供了一种内部bean的形式,即在bean的内部定义另外一个bean,以达到使用目的。这种方式和引用方式的区别在于,引用方式所引用的bean被定义在<beans></beans>之中,这种bean可以被所有的其他定义在beans下的bean所共享;而内部bean,只可以由其外层的bean使用,即独享型的。除此之外,内部bean的使用和定义和一般的bean并无区别。在次,不过不再多阐述。

接下来所说的是Spring对集合类型的装载。体现在四个集合类型中:<list>、<set>、<map>、<props>。<list>和<set>的区别是,<set>不允许其内部存在冗余数据;<map>和<props>的区别在于,<props>内部的name和value必须都是String类型,而<map>则可以是任何类型。其实,这四种定义在xml文件中的集合类型在被解析时,会构造出相应在java中的集合类型,即java.util.*。下面接着上述的例子进行。一般来讲,一部电影会穿插多种音乐。因此,应该将字段music改为集合类型。于是可以设置如下:

  1. <property name="music">  
  2.   <list>  
  3.     <ref bean="EnterLordVader" />  
  4.     <ref bean="ANewHopeandEndCredits" />  
  5.     <!-- <ref bean="" />-->  
  6.   </list>  
  7. </property>  

类似的,<set>的使用完全一样,前提是需要保证内部的数据没有重复。<map>是一组name-value的集合,如果我们List类型换为map类型,则xml中的配置如下:

  1. <property name="music">   
  2.   <map>   
  3.     <entry key="Enter Lord Vader" value="EnterLordVader" />   
  4.     <entry key="A New Hope and End Credits" value="ANewHopeandEndCredits" />   
  5.   </map>   
  6. </property>  

为了方便起见,现将music设想为Properties类型的。于是<props>的使用如下:

  1. <property name="music"> 
  2.     <props> 
  3.       <prop key="EnterLordVade">Enter Lord Vade</prop> 
  4.       <prop key="ANewHopeandEndCredits">A New Hope and End Credits</prop> 
  5.     </props> 
  6.   </property> 

需要注意的是,Spring同时也提供了null的装载。这是由于有些属性在未被赋值之前,需要保持为空。举例如下:

  1. <property name="someNonNullProperty"><null/></property> 

最后要说的当然是SpEL。这一特性在Spring 3中得以引进。它的一般使用方式是:

  1. <property name="count" value="#{5}"/> 

SpEL在以下的点上提供了便捷方式:通过对bean的ID来引用bean;对对象方法的调用和属性访问;提供算术,关系以及逻辑的运算;正则表达式的匹配;对集合的操作。首先,第一点,引用bean:

  1. <property name="ANewHopeandEndCredits" value="#{ANewHopeandEndCredits}"/> 

  1. <property name="ANewHopeandEndCredits" ref="ANewHopeandEndCredits"> 

是等价的。但不同的是,我们可以使用如下方式:

  1. <property name="ANewHopeandEndCredits" value="#{ANewHopeandEndCredits.getName()}"/>  

即调用对象的方法(或可以访问对象的属性值)。这就是上述所说的SpEL的优势之二。在算术、关系和逻辑运算中,类似于在“#{}”中的括号里编写Java代码。拿下例来说:

  1. <property name="average" value="#{counter.total / counter.count}"/> 

这是一个简单的除运算。(有趣的是,SpEL提供了^操作,而Java没有。)关系运算需要注意的地方是,大于或者小于号和xml的书写规则会冲突,于是S平EL提供了eq,lt,le,gt,ge分别表示==,<,<=,>,>=。逻辑运算关键词分别有:and,or,not以及!。注意,S平EL并没有提供“&&”或者“||”符号代表and和or。另外,三木运算方式和Java的书写方式也是类似的。而对于正则表达式匹配,SpEL提供了matches关键字,这里不再过多阐述。

对集合的操作,SpEL提供了类似Java操作集合的机制。下面以一个例子为例:

  1. package com.Spring.cities; 
  2. public class City { 
  3.   private String name; 
  4.   private String province; 
  5.   public String getName() { 
  6.     return this.name; 
  7.   } 
  8.   public String getProvince() { 
  9.     return this.province; 
  10.   } 
  11.   public void setName(String name) { 
  12.     this.name = name; 
  13.   } 
  14.   public void setProvince(String province) { 
  15.     this.province = province; 
  16.   } 

以下代码使用Spring的<util:list>定义了一组城市:

  1. <util:list id="cities"> 
  2.   <bean class="com.Spring.cities.City" 
  3.      p:name="Chengdu" p:province="Sichuan" /> 
  4.   <bean class="com.Spring.cities.City" 
  5.      p:name="Guangzhou" p:province="Guangdong" /> 
  6.   <bean class="com.Spring.cities.City" 
  7.      p:name="Xian" p:province="Shanxi" /> 
  8.   <bean class="com.Spring.cities.City" 
  9.      p:name="Yinchuan" p:province="Ningxia" /> 
  10. </util:list> 

此时,我们就可以使用SpEL筛选具体的城市:

  1. <property name="chosenCity" value="#{cities[2]}"/> 

与使用<util:list>类似,我们也可以这样使用<util:map>,通过name来得到value。除了直接使用“.[]”的方式过滤外,还有“.^[]”和“.$[]”两种方式,分别表示选择第一个和最后一个匹配的项。最后要说的一个是“.![]”,它用于支持集合的映射。

  1. <property name="cityNames" value="#{cities.![name]}"/> 

当只是需要得到城市名,而不需要得到全部的城市信息时,使用以上代码,可以直接映射之前所定义的城市集合中,各城市的名称部分信息集合。

需要注意的是,虽然使用SpEL可以减少Java代码的编写工作,但是过多的使用会带来一些负面的影响。这种方式不好编写测试代码,同时也没有IDE的支持,以检查语法问题。

至此,对bean的基本使用方式已经介绍得差不多了。