最小化Spring XML配置

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

最小化Spring XML配置

benjaminwhx 2016-05-19 15:21:33 浏览1444
展开阅读全文

spring提供了几种技巧,可以帮助我们减少XML的配置数量:

1、自动装配(autowiring)有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。

2、自动检测(autodiscovery)比自动装配更进了一步,让Spring能够自动识别哪些类需要被配置成Spring Bean,从而减少对<bean>元素的使用。

 

1.1、自动装配Bean属性

1.1.14种类型的自动装配

1、byName-把与Bean的属性具有相同名字的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。

2、byType-把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的类型相匹配的Bean,则该属性不被装配。

3、constructor-把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。

4、autodetect-首先尝试使用constructor进行自动装配。如果失败,再尝试使用byType进行自动装配。


byName自动装配

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

可以这么写

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <bean id="instrument" class="com.springinaction.springidol.Saxophone"/>  
  2.     <bean id="kenny2" class="com.springinaction.springidol.Instrumentalist" autowire="byName">  
  3.         <property name="song" value="Jingle Bells"></property>  
  4.     </bean>  

byType自动装配

byType自动装配的工作方式类似于byName自动装配,只不过不再是匹配属性的名字而是检查属性的类型。当我们尝试使用byType自动装配时,Spring会寻找哪一个Bean的类型与属性的类型相匹配。

但是byType自动装配存在一个局限性:如果Spring寻找到多个Bean,它们的类型与需要自动装配的属性的类型都相匹配,怎么办呢?在这种场景下,Spring不会猜测哪一个Bean更适合自动装配,而是选择抛出异常。所以,应用只允许存在一个Bean与需要自动装配的属性类型相匹配。

为了避免因为使用byType自动装配而带来的歧义,Spring为我们提供了另外两种选择:可以为自动装配表识一个首选的Bean,或者可以取消某个Bean自动装配的候选资格。

为自动装配标识一个首选Bean,可以使用<bean>元素的primary属性。如果只有一个自动装配的候选Bean的primary属性设为true,那么该Bean将比其他候选Bean优先被选择。

但是primary属性有个很怪异的一点:它默认设置为true。所以,为了使用primary属性,我们不得不将所有非首选的primary属性设为false。例如:

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <bean id="instrument" class="com.springinaction.springidol.Saxophone" primary="false"/>  

primary属性仅对标识首选Bean有意义。如果在自动装配时,我们希望排除某些Bean,那可以设置这些Bean的autowire-candidate属性为false,如下所示:

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <bean id="instrument" class="com.springinaction.springidol.Saxophone" autowire-candidate="false"/>  

constructor装配

constructor自动装配具有和byType自动装配相同的局限性。当发现多个Bean匹配某个构造器的入参时,Spring不会尝试猜测哪一个Bean更适合自动装配。此外,如果一个类有多个构造器,它们都满足自动装配的条件时,Spring也不会尝试猜测哪一个构造器更适合使用。


最佳自动装配

当配置一个Bean的autowire属性为autodetect时,Spring将首先使用constructor自动装配,如果没有发现与构造器相匹配的Bean时,Spring将尝试使用byType自动装配。


1.1.2、默认自动装配

我们所需要做的仅仅是在根元素<beans>上增加一个default-autowire属性:

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  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.     </span><span style="color:#ff0000;">default-autowire="byType"</span><span style="color:#009900;">></span>  

default-autowire应用于指定spring配置文件中的所有Bean,而不是应用于Spring应用上下文中的所有Bean。

你可以在一个Spring应用上下文中定义多个配置文件,每一个配置文件都可以有自己的默认自动装配策略。

同样,不能因为我们配置了一个默认的自动装配策略,就意味着所有的Bean都只能使用这个默认的自动装配策略。我们还可以使用<bean>元素的autowire属性来覆盖<beans>元素所配置的默认自动装配策略。


1.2、使用注解装配

Spring容器默认禁用注解装配。所以,在使用基于注解的自动装配前,我们需要在Spring配置中启用它。最简单的启用方式是使用Springcontext命名空间配置中的<context:annotation-config>元素:

[html] view plain copy
 print?
  1. <context:annotation-config/>  

这个元素告诉Spring我们打算使用基于注解的自动装配。一旦配置完成,我们就可以对代码添加注解,标识Spring应该为属性、方法和构造器进行自动装配。

Spring3支持几种不同的用于自动装配的注解:

1、Spring自带的@Autowired注解;

2、JSR-330的@Inject注解

3、JSR-250的@Resource注解。


1.2.1、使用@Autowired

@Autowired注解特别有趣的地方在于,我们不仅能使用它标注setter方法,还可以标注需要自动装配Bean引用的任意方法:

[html] view plain copy
 print?
  1. @Autowired  
  2.     public void setInstrument(Instrument instrument){  
  3.         this.instrument = instrument ;  
  4.     }  

[html] view plain copy
 print?
  1. @Autowired  
  2.     public void heresYourInstrument(Instrument instrument){  
  3.         this.instrument = instrument ;  
  4.     }  

@Autowired注解甚至可以标注构造器

[html] view plain copy
 print?
  1. @Autowired  
  2.     public Instrumentalist(Instrument instrument){  
  3.         this.instrument = instrument ;  
  4.     }  

另外,我们还可以使用@Autowired注解直接标注属性。

[html] view plain copy
 print?
  1. @Autowired  
  2.     private Instrument instrument ;  

但是@Autowired也有存在的限制:在应用中,必须只能有一个Bean适合装配到@Autowired注解所标注的属性或参数中。如果没有匹配的Bean,或者存在多个匹配的Bean,@Autowired注解就会遇到一些麻烦。幸运的是,上面的这两种场景都有相应的解决办法。首先,看一下如果没有匹配的Bean,如何让@Autowired注解远离失败。


可选的自动装配

属性不一定非要装配,null值也是可以接受的。在这种场景下,可以通过设置@Autowired的required属性为false来配置自动装配是可选的。

[html] view plain copy
 print?
  1. @Autowired(required=false)  
  2.     private Instrument instrument ;  

此外,当使用@Autowired标注多个构造器时,Spring就会从所有满足装配条件的构造器中选择入参最多的那个构造器。


限制奇异性的依赖

为了帮助@Autowired鉴别出哪一个Bean才是我们所需要的,我们可以配合使用Spring的@Qualifier注解

例如下面我们可以使用@Qualifier来明确指定名为guitar的Bean:

[html] view plain copy
 print?
  1. @Autowired  
  2.     @Qualifier("guitar")  
  3.     private Instrument instrument ;  

@Qualifier注解真正缩小了自动装配挑选Bean的范围。


创建自定义的限定器(Qualifier)

为了创建一个自定义的限定器注解,我们所需要的仅仅是定义一个注解,并使用@Qualifier注解座位它的元注解。例如,让我们创建自己的@StringedInstrument注解来充当限定器。

[html] view plain copy
 print?
  1. import java.lang.annotation.ElementType;  
  2. import java.lang.annotation.Retention;  
  3. import java.lang.annotation.RetentionPolicy;  
  4. import java.lang.annotation.Target;  
  5.   
  6. import org.springframework.beans.factory.annotation.Qualifier;  
  7.   
  8. @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})  
  9. @Retention(RetentionPolicy.RUNTIME)  
  10. @Qualifier  
  11. public @interface StringedInstrument {  
  12.   
  13. }  

通过自定义的@StringedInstrument注解,我们现在可以用它来代替@Qualifier来标注Guitar:

[html] view plain copy
 print?
  1. @StringedInstrument  
  2. public class Guitar implements Instrument{  
  3.     ...  
  4. }  

现在,我们也可以使用@StringedInstrument对自动装配的instrument属性进行限定:

[html] view plain copy
 print?
  1. @Autowired  
  2.     @StringedInstrument  
  3.     private Instrument instrument ;  

当Spring尝试装配instrument属性时,Spring会把所有可选择的乐器Bean缩小到只有被@StringedInstrument注解所标注的Bean。如果只有一个乐器Bean使用@StringedInstrument注解,那么该Bean将会被装配到instrument属性中。

如果使用@StringedInstrument注解的乐器Bean有多个,我们还需要进一步限定来缩小范围。例如,假设除了Guitar Bean,我们还有一个HammeredDulcimer Bean也需要@StringedInstrument注解。所以,为了进一步限定Guitar Bean,我们可以定义另一种限定器注解@Strummed:

[html] view plain copy
 print?
  1. @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. @Qualifier  
  4. public @interface Strummed {  
  5.   
  6. }  

现在,我们可以使用@Strummed注解标注instrument属性把选择范围缩小:

[html] view plain copy
 print?
  1. @Autowired  
  2.     @StringedInstrument  
  3.     @Strummed  
  4.     private Instrument instrument ;  

Spring的@Autowired注解是减少Spring XML配置的一种方式。但是使用它的类会引入对Spring的特定依赖。幸运的是,Spring还提供了标准的java注解来替代@Autowired。让我们了解一下如何使用Java依赖注入标准中的@Inject。


1.2.2、借助@Inject实现基于标准的自动装配

@Autowired一样,@Inject可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Inject没有required属性。因此,@Inject注解所标注的依赖关系必须存在,如果不存在,则会抛出异常。

相对于@Autowired所对应的@Qualifier@Inject所对应的是@Named注解。

@Named注解的工作方式非常类似于Spring@Qualifier,正如我们在这里所看到的:

[html] view plain copy
 print?
  1. @Inject  
  2.     @Named("guitar")  
  3.     private Instrument instrument ;  

Spring的@Qualifier与JSR-330的@Named的关键区别在于语义层面。@Qualifier注解帮助我们缩小所匹配的Bean的选择范围(默认使用Bean的ID),而@Named通过Bean的ID来标识可选择的Bean。


创建自定义的JSR-330 Qualifier

事实上,JSR-330在javax.inject包里有自己的@Qualifier注解。不像Spring的@Qualifier,JSR-330不建议使用该注解。相反,JSR-330鼓励我们使用该注解来创建自定义的限定器注解,就像我们使用Spring的@Qualifier来创建自定义注解一样。

例如,下面的程序战士了一个新的@StringInstrument注解,该注解使用JSR-330的@Qualifier来创建的,取代了之前使用Spring的@Qualifier

[html] view plain copy
 print?
  1. import java.lang.annotation.ElementType;  
  2. import java.lang.annotation.Retention;  
  3. import java.lang.annotation.RetentionPolicy;  
  4. import java.lang.annotation.Target;  
  5.   
  6. import javax.inject.Qualifier;  
  7.   
  8. @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})  
  9. @Retention(RetentionPolicy.RUNTIME)  
  10. @Qualifier  
  11. public @interface StringedInstrument {  
  12. }  


1.2.3、在注解注入中使用表达式

Spring3.0引入了@Value,它是一个新的装配注解,可以让我们使用注解装配String类型的值和基本类型的值,例如intboolean

我们可以通过@Value直接标注某个属性、方法或者方法参数,并传入一个String类型的表达式来装配属性。例如:

[html] view plain copy
 print?
  1. @Value(“Eruption”)  
  2. private String song ;  

在这里,我们为String类型的属性装配了一个String类型的值。但是传入@Value的String类型的参数只是一个表达式--它的计算结果可以是任意类型,因此@Value能够标注任意类型的属性。

不过,借助SpEL表达式,@Value被赋予了魔力。这一特性也使得@Value注解成为强大的装配可选方案。

例如,与其为song属性硬编码一个静态值,不如使用SpEL从系统属性中获取一个值:

[html] view plain copy
 print?
  1. @Value(“#{systemProperties.myFavoriteSong}”)  
  2. private String song ;  



1.3、自动检测Bean

<context:component-scan>元素除了完成与<context:annotation-config>一样的工作,还允许Spring自动检测Bean和定义Bean,这意味着不使用<bean>元素,Spring应用中的大多数Bean都能够实现定义和装配。

[html] view plain copy
 print?
  1. <context:component-scan base-package="com.wiseweb.pom" />  


<context:component-scan>元素会扫描指定的包及其所有子包,并查找出能够自动注册为Spring bean的类。base-package属性标识了<context:component-scan>元素所扫描的包。

那么<context:component-scan>又是如何知道哪些类需要注册为Spring Bean呢?


1.3.1、为自动检测标注Bean

默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,这些特殊的注解如下。

1、@Component:通用的构造型注解,标识该类为Spring组件。

2、@Controller:标识将该类定义为Spring MVC controller

3、@Repository:标识将该类定义为数据仓库。

4、@Service:标识将该类定义为服务。

使用@Component标注的任意自定义注解。

当使用<context:component-scan>时,基于注解地自动检测只是一种扫描策略。让我们了解下如何配置<context:component-scan>使用其他扫描策略来查找候选Bean

 

1.3.2、过滤组件扫描

事实上,在如何扫描来获得候选Bean方面,<context:component-scan>非常灵活。通过为<context:component-scan>配置<context:include-filter>/或者<context:exclude-filter>子元素,我们可以随意调整扫描行为。

为了掩饰组件扫描的过滤机制,考虑一下如何给予注解让<context:component-scan>自动注册所有市县Instrument接口的类。我们不得不浏览每一个Instrument实现的源码,并且使用@Component标注它们。至少,这是极为不便的,如果使用了第三方的Instrument实现,或许都没有源码的访问权限来添加注解。

所以,我们替换掉基于注解的组件扫描策略,再增加一个包含过滤器来要求<context:component-scan>自动注册所有的Instrument实现类,如下所示:

[html] view plain copy
 print?
  1. <context:component-scan base-package="com.wiseweb.pom" >  
  2.         <context:exclude-filter type="assignable" expression="com.springinaction.springidol.Instrument"/>  
  3.     </context:component-scan>  

在这种情况下,我们要求派生于Instrument的所有类自动注册为Spring Bean。我们还可以选择如下任一一种过滤器。如下所示:

1、annotation:过滤器扫描使用指定注解所标注的那些类。通过expression属性指定要扫描的注解。

2、assignable:过滤器扫描派生于expression属性所指定类型的哪些类

3、aspectj:过滤器扫描与expression属性所指定的AspectJ表达式所匹配的哪些类。

4、custom:使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定。

5、regex:过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的那些类


除了使用<context:include-filter>告知<context:component-scan >哪些类需要注册为Spring Bean以外,我们还可以使用<context:exclude-filter>来告知<context:component-scan >哪些类不需要注册为Spring Bean。

网友评论

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