装配Bean

  1. 云栖社区>
  2. 博客>
  3. 正文

装配Bean

benjaminwhx 2016-05-19 15:20:58 浏览1002
展开阅读全文

1、创建Spring配置

XML文件中声明Bean时,spring配置文件的根元素来源于Spring beans命名空

间所定义的<beans>元素。以下为一个典型的Spring XML配置文件:

[html] view plain copy
 print?
  1. <span style="color:#009900;"><?xml version="1.0" encoding="utf-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/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 declarations go here -->  
  7. </beans></span>  

在<beans>元素内,你可以防止所有的Spring配置信息,包括<bean>元素的声明。但是beans命名空间并不是你遇到的唯一的Spring命名空间。Spring的核心框架自带了10个命名空间配置。

1、aop:为声明切面以及将@AspectJ注解的类代理为Spring切面提供了配置元素。

2、beans:支持声明Bean和装配Bean,是SPring最核心也是最原始的命名空间。

3、context:为配置Spring应用上下文提供了配置元素,包括自动监测和自动装配Bean。注入非Spring直接管理的对象。

4、jee:提供了与Java EE API的集成,例如JNDI和EJB。

5、jms:为声明消息驱动的POJO提供了配置元素。

6、lang:支持配置由Groovy、JRuby或BeanShell等脚本实现的Bean

7、mvc:启用Spring MVC的能力,例如面向注解的控制器、试图控制器和拦截器。

8、oxm:支持Spring的对象到XML映射配置

9、tx:提供声明式事务配置

10、util:提供各种各样的工具类元素,包括把集合配置为Bean、支持属性占位符元素


2、构造器注入Bean

下面是一个Juggling Bean以及简单的Bean的声明结构

Performer表演者接口:

[html] view plain copy
 print?
  1. package com.springinaction.springidol;  
  2. //表演者接口  
  3. public interface Performer {  
  4.     //表演方法  
  5.     void perform() throws performanceException ;  
  6. }  


Juggling Bean

[html] view plain copy
 print?
  1. package com.springinaction.springidol;  
  2.   
  3. public class Juggler implements Performer{  
  4.     private int beanBags = 3 ;  
  5.       
  6.     public Juggler(){  
  7.           
  8.     }   
  9.       
  10.     public Juggler(int beanBags){  
  11.         this.beanBags = beanBags ;  
  12.     }  
  13.     @Override  
  14.     public void perform() throws performanceException {  
  15.         System.out.println("JUGGLING " + beanBags + " BEANBAGS");  
  16.     }  
  17.   
  18. }  

Bean的简单声明

[html] view plain copy
 print?
  1. <bean id="duke" class="com.springinaction.springidol.Juggler" />  

上面这个bean使用了默认的无参构造器,如果要让Duke同时抛N个豆袋子的话,必须使用以下声明

[html] view plain copy
 print?
  1. <bean id="duke" class="com.springinaction.springidol.Juggler" >  
  2.         <constructor-arg value="15"/>  
  3.     </bean>  

构造器还可以注入对象的引用

如果再Juggler类中再定义一个构造器,要传入两个参数,一个是Poem接口,一个是int类型数据,那么bean的声明就可以这么写:

[html] view plain copy
 print?
  1. <bean id="duke" class="com.springinaction.springidol.Juggler" >  
  2.         <constructor-arg value="15"/>  
  3.         <constructor-arg ref="sonnet29"/>  
  4.     </bean>  

(注:sonnet29是Poem接口的实现)


通过工厂方法创建Bean

下面是一个Stage单例类

[html] view plain copy
 print?
  1. package com.springinaction.springidol;  
  2.   
  3. public class Stage {  
  4.     private Stage(){  
  5.     }  
  6.       
  7.     private static class stageSingletonHolder{  
  8.         static Stage instance = new Stage() ;  
  9.     }  
  10.       
  11.     public static Stage getInstance(){  
  12.         return stageSingletonHolder.instance ;  
  13.     }  
  14. }     

为了在上下文中将Stage配置为Bean,可以像下面的配置来使用factory-method;

[html] view plain copy
 print?
  1. <bean id="theStage" class="com.springinaction.springidol.Stage"  
  2.         factory-method="getInstance" />  


3、Bean的作用域

Bean有以下几种作用域:

1、singleton:在每一个Spring容器中,一个Bean定义只有一个对象实例

2、prototype:允许Bean的定义可以被实例化任意次(每次调用都创建一个实例)

3、request:在一次HTTP请求中,每个Bean定义对应一个实例,该作用域在基于WebSpring上下文(例如Spring MVC)中才有效。

4、session:在一个HTTP Session中,每个Bean定义对应一个实例,该作用域仅在基于WebSpring上下文(例如Spring MVC)中才有效。

5、global-session:再一个全局HTTP Session中,每个Bean定义对应一个实例,该作用域仅在Portlet上下文中才有效。

大多数情况下,我们只需要选择默认的singleton作用域即可,但是如果我们使用Spring做为工厂来创建领域对象新实例时,prototype作用域就非常有用。如果领域对象的作用域配置为prototype,我们在Spring中可以很容易地配置它们,就像配置其他Bean一样。Spring保证每次请求一个prototypr Bean时总是返回一个独一无二的实例。


初始化和销毁Bean

在bean内我们可以使用init-method和destroy-method属性来声明auditorium Bean:

[html] view plain copy
 print?
  1. <bean id="auditorium" class="com.springinaction.springidol.Auditorium"  
  2.         init-method="turnOnLights" destroy-method="turnOffLights" />  

当我们使用这种方式配置时,auditorium Bean实例化后会立即调用turnOnLights()方法,在Bean从容器移除和销毁前,会调用turnOffLights()方法。

还有一种初始化和销毁Bean的方法:

初始化:让需要实现init-method的类实现InitializingBean接口,它有一个afterPropertiesSet方法,把init-method方法中的内容写入afterPropertiesSet()中,效果与上面一样。

销毁:让需要实现destroy-method的类实现DisposableBean接口,它有一个destroy()方法,把destroy-method方法中的内容写入destroy()中,效果与上面一样。


默认的init-method和destroy-method

如果在上下文中定义的很多Bean都拥有相同名字的初始化方法和销毁方法,你可以使用<beans>元素的dafault-init-method和default-destory-method属性:

[html] view plain copy
 print?
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  4.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"  
  5. default-init-method="turnOnLights"  
  6. default-destroy-method="turnOffLights">  


4、注入Bean属性

注入简单的值只需要在Bean中加入property属性

下面是一个“天才音乐家”的类

[html] view plain copy
 print?
  1. package com.springinaction.springidol;  
  2.   
  3. public class Instrumentalist implements Performer{  
  4.     public Instrumentalist(){  
  5.     }  
  6.       
  7.     @Override  
  8.     public void perform() throws performanceException {  
  9.         System.out.println("Playing " + song + " : ");  
  10.     }  
  11.       
  12.     private String song ;  
  13.   
  14.     public String getSong() {  
  15.         return song;  
  16.     }  
  17.   
  18.     public void setSong(String song) {  
  19.         this.song = song;  
  20.     }  
  21.       
  22.     public String screamSong(){  
  23.         return song ;  
  24.     }  
  25.       
  26.     private Instrument instrument ;  
  27.       
  28.     public void setInstrument(Instrument instrument){  
  29.         this.instrument = instrument ;  
  30.     }  
  31.       
  32.       
  33.   
  34. }  

注入简单的值可以直接用property属性:

[html] view plain copy
 print?
  1. <bean id="kenny" class="com.springinaction.springidol.Instrumentalist">  
  2.         <property name="song" value="Jingle Bells" />  
  3.     </bean>  

还可以应用其他的Bean:

[html] view plain copy
 print?
  1. <property name="instrument" ref="saxophone" />  

使用Spring的命名空间p装配属性

使用<property>元素为Bean的属性装配和引用并不太复杂。尽管如此,Spring的命名空间p提供了另一种Bean属性的装配方式,该方式不需要配置如此多的尖括号。

命名空间p的schema URI为http://www.springframework.org/schema/p。

下面是一段示例bean配置:

[html] view plain copy
 print?
  1. <bean id="kenny" class="com.springinaction.springidol.Instrumentalist"   
  2.         p:song="Jingle Bells"  
  3.         p:instrument-ref="saxophone"/>  

装配集合

Java自带了多种集合类,Spring也提供了相应的集合配置元素

1、<list>:装配list类型的值,允许重复

2、<set>:装配set类型的值,不允许重复

3、<map>:装配map类型的值,名称和值可以是任意类型

4、<props>:装配properties类型的值,名称和值必须都是String型


装配List集合

[html] view plain copy
 print?
  1. <bean id="hank" class="com.springinaction.springidol.OneManBand">  
  2.         <property name="instruments">  
  3.             <list>  
  4.                 <ref bean="guitar"/>  
  5.                 <ref bean="cymbal"/>  
  6.                 <ref bean="harmonica"/>  
  7.             </list>  
  8.         </property>  
  9.     </bean>  

装配set集合

[html] view plain copy
 print?
  1. <bean id="hank" class="com.springinaction.springidol.OneManBand">  
  2.         <property name="instruments">  
  3.             <set>  
  4.                 <ref bean="guitar"/>  
  5.                 <ref bean="cymbal"/>  
  6.                 <ref bean="harmonica"/>  
  7.             </set>  
  8.         </property>  
  9.     </bean>  

装配map集合

[html] view plain copy
 print?
  1. <bean id="hank" class="com.springinaction.springidol.OneManBand">  
  2.         <property name="instruments">  
  3.             <map>  
  4.                 <entry key="GUITAR" value-ref="guitar"/>  
  5.                 <entry key="CYMBAL" value-ref="cymbal"/>  
  6.                 <entry key="HARMONICA" value-ref="harmonica"/>  
  7.             </map>  
  8.         </property>  
  9.     </bean>  

<map>中的<entry>元素由一个键和一个值组成,键和值可以是简单类型,也可以是其他Bean的引用。这些属性将帮助我们指定<entry>的键和值

1、key:指定map中entry的键为String

2、key-ref:指定map中entry的键为String上下文中其他Bean的引用

3、value:指定map中entry的值为String

4、value-ref:指定map中entry的值为Spring上下文中其他Bean的引用


装配Properties集合

[html] view plain copy
 print?
  1. <bean id="hank" class="com.springinaction.springidol.OneManBand">  
  2.         <property name="instruments">  
  3.             <props>  
  4.                 <prop key="GUITAR">STRUM STRUM STRUM</prop>  
  5.                 <prop key="GUITAR">CRASH CRASH CRASH</prop>  
  6.                 <prop key="GUITAR">HUM HUM HUM</prop>  
  7.             </props>  
  8.         </property>  
  9.     </bean>  

装配空值

为属性设置null值,只需要使用<null/>元素。

例如:

[html] view plain copy
 print?
  1. <property name="someNonNullProperty"><null/></property>  


5、使用表达式装配

SpEL的基本原理:

SpEL表达式的首要目标是通过计算获得某个值。在计算这个数值的过程中,会使用到其他的值并会对这些值进行操作。最简单的SpEL求职或许是对字面值、Bean的属性或者某个类的常量进行求值。

字面值

下面的value属性中使用#{}界定符把这个值装配到Bean的属性中,输出5

[html] view plain copy
 print?
  1. <property name="count" value="#{5}" />  
浮点数字一样可以出现在SpEL表达式中

[html] view plain copy
 print?
  1. <property name="frequency" value="#{89.7}" />  

String类型的字面值可以使用单引号或双引号作为字符串的界定符,例如:

[html] view plain copy
 print?
  1. <property name="name" value="#{'Chuck'}" />  

或者使用单引号作为XML属性的界定符,则可以在SpEL表达式中使用双引号:

[html] view plain copy
 print?
  1. <property name="name" value='#{"Chuck"}' />  

还可以使用的另外两个字面值是布尔型的true和false。

[html] view plain copy
 print?
  1. <property name="name" value="#{false}" />  

虽然大家看起来SpEL表达式中仅包含字面值没有太多用处。但是请记住复杂的SpEL表达式通常是由简单的表达式构成的。所以了解如何在SpEL中使用字面值是很有意义的。当表达式变得越来越复杂时,我们最终还是会用到字面值表达式。


引用Bean、Properties和方法

我们需要在SpEL表达式中使用Bean ID将一个Bean装配到另一个Bean的属性中:

[html] view plain copy
 print?
  1. <span style="color:#009900;"><property name="instrument" value="#{saxophone}" /></span>  

[html] view plain copy
 print?
  1. <span style="color:#009900;"><property name="instrument" ref="saxophone" /></span>  

这两个其实是等价的。

我们还可以通过SpEL表达式使用Bean的引用来获取Bean的属性

[html] view plain copy
 print?
  1. <span style="color:#009900;"><property name="song" value="#{kenny.song}" /></span>  

我们能对Bean所能做到的事情并不只是引用它的属性,我们还可以调用它的方法。

[html] view plain copy
 print?
  1. <span style="color:#009900;"><property name="song" value="#{songSelector.selectSong()}" /></span>  

现在我们假设方法返回值大写的,我们所要做的就是对返回的值调用toUpperCase()方法,如下:

[html] view plain copy
 print?
  1. <span style="color:#009900;"><property name="song" value="#{songSelector.selectSong().toUpperCase()}" /></span>  

每次都能打到预期目标...只要selectSong()方法不返回null。如果selectSong()真正返回一个null值,那么SpEL表达式求值时会抛出一个NullPointerException异常。

在SpEL中避免抛出讨厌的空指针异常(NullPointerException)的方法是使用null-safe存储器:

[html] view plain copy
 print?
  1. <property name="song" value="#{songSelector.selectSong()?.toUpperCase()}"></property>  

我们使用?.运算符代替点(.)来访问toUpperCase()方法。在访问右边方法之前,该运算符会确保左边项的值不会为null。所以,如果selectSong()为null值,SpEL不再尝试调用toUpperCase()方法。


操作类

在SpEL中,使用T()运算符会调用类作用域的方法和常量

例如,在SpEL中,使用JAVA的Math类,我们可以这么写:

T(java.lang.Math)

但是T()运算符的真正价值在于,通过该运算符可以访问指定类的静态方法和常量。

下面是引用Math中的PI常量。

[html] view plain copy
 print?
  1. <property name="nultiplier" value="#{T(java.lang.Math).PI}" />  

将一个随机数(0到1)装配到Bean的一个属性中。

[html] view plain copy
 print?
  1. <property name="randomNumber" value="#{T(java.lang.Math).random()}" />  

在SpEL值上执行操作

SpEL提供的运算符:

1、算数运算:+、-、*、/、%、^

2、关系运算:<、>、==、<=、>=、lt、gt、eq、le、ge

3、逻辑运算:and、or、not、|

4、条件运算:?:(ternary)、?:(Elvis)

5、正则表达式:matches


使用SpEL进行数值运算:

两个数字相加:

[html] view plain copy
 print?
  1. <property name="adjustedAmount" value="#{counter.total + 42}" />  

*运算符执行乘法运算

[html] view plain copy
 print?
  1. <property name="circumference" value="#{2 * T(java.lang.Math).PI * circle.radius}" />  

比较两个值是否相等

[html] view plain copy
 print?
  1. <property name="equals" value="#{counter.total == 100}" />  

 在Spring的XML配置文件中使用小于等于和大于等于符号时,会报错,这是因为这两个符号在XML中有特殊含义。当在XML中使用SpEL时,最好对这些运算符使用SpEL的文本替代方式。

[html] view plain copy
 print?
  1. <property name="hasCapacity" value="#{counter.total le 100000}" />  

SpEL提供了多种运算符,你可以使用它们来对表达式进行求值

1、等于:==  eq

2、小于:<    lt

3、小于等于<=   le

4、大于:>   gt

5、大于等于:>=  g

开发者更倾向于使用文本运算符,而不是符号型运算符。

SpEL提供了多种运算符,你可以使用它们来对表达式进行求值

1、and:逻辑and运算符,只有运算符两边都是true,表达式才能是true

2、or:逻辑or运算符,只有运算符的任意一边是true,表达式就会是true

3、not或!:逻辑Not运算操作,对运算结果求反。


使用and运算符:

[html] view plain copy
 print?
  1. <property name="largeCircle" value="#{shape.kind == 'circle and shape.perimeter gt 10000'}" />  

三元运算符?:

我们可以用三元运算符检查一个值是否为null

[html] view plain copy
 print?
  1. <property name="song" value="#{kenny.song != null ? kenny.song : 'Greensleeves'}" />  

SpEL的正则表达式

为了进一步解释matches运算符,假设我们想判断一个字符串是否是有效的邮件地址。在这种情况下,我们可以使用matches运算符,如下所示:

[html] view plain copy
 print?
  1. <property name="validEmail" value="#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]\\.com'}" />  

在SpEL中筛选集合

我们可以通过SpEL引用集合中的某个成员,就像在java里操作一样。

下面定义了一个City类

[html] view plain copy
 print?
  1. public class City{  
  2.   private String name ;  
  3.   private String state ;  
  4.   private int population ;  
  5. }  

同样我们使用<util:list>元素在Spring里配置了一个包含City对象的List集合。

[html] view plain copy
 print?
  1. <util:list id="cities">  
  2.         <bean class="com.habuma.spel.cities.City" p:name="Chicago" p:state="IL" p:population="2853114"/>  
  3.         <bean class="com.habuma.spel.cities.City" p:name="Atlanta" p:state="GA" p:population="53322"/>  
  4.         <bean class="com.habuma.spel.cities.City" p:name="Dallas" p:state="TX" p:population="43441"/>  
  5.         <bean class="com.habuma.spel.cities.City" p:name="Houston" p:state="TX" p:population="2323"/>  
  6.         <bean class="com.habuma.spel.cities.City" p:name="Odessa" p:state="TX" p:population="11233"/>  
  7.         <bean class="com.habuma.spel.cities.City" p:name="El Paso" p:state="TX" p:population="42523"/>  
  8.         <bean class="com.habuma.spel.cities.City" p:name="Jal" p:state="NM" p:population="6745"/>  
  9.         <bean class="com.habuma.spel.cities.City" p:name="Las Cruces" p:state="NM" p:population="965456"/>  
  10.     </util:list>  

我们可以通过SpEL访问集合成员

下面的SpEL表达式就是访问cities集合中的第3个city

[html] view plain copy
 print?
  1. <property name="chosenCity" value="#{cities[2]}" />  

假设你随机选择一个city:

[html] view plain copy
 print?
  1. <property name="chosenCity" value="#{cities[T(java.lang.Math).random() * cities.size()]}" />  

[]运算符同样可以用来获取java.util.Map集合中的成员。例如,假设city对象以其名字作为键放入Map集合中。在这种场景下,我们就可以像下面所展示的那样获取键为Dallas的entry

[html] view plain copy
 print?
  1. <property name="chosenCity" value="#{cities['Dallas']}" />   

[]运算符的另一种用法是从java.util.Properties集合中获取值。例如,假设我们需要通过<util:properties>元素在Spring中家在一igeproperties配置文件,如下所示:

[html] view plain copy
 print?
  1. <span style="color:#009900;"><util:properties id="settings" location="classpath:settings.properties"/></span>  

除了访问<util:properties>所声明集合中的属性,Spring还未SpEL创造了两种特殊的选择属性的方式:systemEnvironment和systemProperties。

我们可以将用户home目录的路径注入到一个bean的属性中:

[html] view plain copy
 print?
  1. <property name="homePath" value="#{systemEnvironment['HOME']} "/>  

[]运算符同样可以通过索引来得到字符串的某个字符。例如,下面的表达式将返回"s":

[html] view plain copy
 print?
  1. 'This is a test'[3]  

查询集合成员

如果我们想从城市(city)集合中查询人口多于100000的城市,一种实现方式是将所有的cities Bean都装配到Bean的属性中,然后在该Bean中增加过滤不符合条件的城市的luoji.danshi在SpEL中,只需使用一个查询运算符(.?[])就可以简单做到。例如:

[html] view plain copy
 print?
  1. <property name="bigCities" value="#{cities.?[population gt 10000000]]} "/>  

SpEl还提供了两种其他查询运算符:".^[]"和".$[]",从集合中查询出第一个匹配项和最后一个匹配项。

[html] view plain copy
 print?
  1. <property name="bigCities" value="#{cities.^[population gt 10000000]]} "/>  

[html] view plain copy
 print?
  1. <property name="bigCities" value="#{cities.$[population gt 10000000]]} "/>  


投影集合

集合投影是从集合的每一个成员中选择特定的属性放入一个新的集合中。SpEL的投影运算符(.![])完全可以做到这一点。

假如,假设我们仅仅需要包含城市名称的一个String类型的集合,而不是City对象的集合。为了实现这样的集合,我们可以这么装配

[html] view plain copy
 print?
  1. <property name="cityNames" value="#{cities.![name]} "/>  

这个表达式的结果是cityNames属性将被赋予一个String类型的集合,包含Chicago、Atlanta、Dallas诸如此类的值。在中括号内name属性决定了结果集合中要包含什么样的成员。

我们还可以这么写:

[html] view plain copy
 print?
  1. <property name="cityNames" value="#{cities.![name + ', ' + state]} "/>  

现在cityNames属性将被赋予了一个集合,这个集合包含诸如"Chicago,IL"、"Atlanta,GA"、和"Dallas,TX"这样的值。

最后一个SpEL的技巧,可以对集合进行查询和投影运算。这里只把符合条件的大城市的名字作为集合注入到cityNames属性中:

[html] view plain copy
 print?
  1. <span style="color:#009900;"><property name="cityNames" value="#{cities.?[population gt 100000].![name + ', ' + state]}"></span>  




总结:Spring容器是Spring框架的核心。Spring自带了多种容器的实现,但是它们可以归为两类。BeanFactory是最简单的容器,提供基础的依赖注入和Bean装配服务。当我们需要更高级的框架时,选择Spring的ApplicationContext做为容器更为合适。

网友评论

登录后评论
0/500
评论
benjaminwhx
+ 关注