最小化Spring XML配置

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

  • 自动装配(autowiring)有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。
  • 自动检测(autodiscovery)比自动装配更进了一步,让Spring能够自动识别哪些类需要被装配成Spring Bean,从而减少对<bean>元素的使用。

当自动装配和自动检测一起使用时,它们可以显著减少Spring的XML配置数量。通常只需要配置少量的几行XML代码,而无需知道在Spring的应用上下文中究竟有多少Bean。

自动装配Bean属性

自动装配(autowiring)

4种类型的自动装配

当涉及自动装配Bean的依赖关系时,Spring有多种处理方式。因此,Spring提供了4种各具特色的自动装配策略。

  • byName——把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。
  • byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的类型相匹配的Bean,则该属性不被装配。
  • constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。
  • autodetect——首先尝试使用constructor进行自动装配。如果失败,再尝试使用byType进行自动装配。

byName自动装配

举个栗子:

<bean id="kenny2"
      class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
    <property name="instrument" ref="saxophone" />
</bean>

在这里,我们使用<property>元素显式装配了Kenny的instrument属性。假设使用<bean>元素在定义萨克斯(saxophone)时,把Bean的id属性设置为instrument:

<bean id="instrument"
        class="com.springinaction.springIdol.Saxphone" />

在本示例中,萨克斯(saxophone)Bean的id属性与Keeny Bean的instrument属性的名字是一样的。通过配置autowire属性,Spring就可以利用此信息自动装配kenny的instrument属性

<bena id="kenny"
      class="com.springinaction.springidol.Instrumentlist"
      autowire="byName">
<property name="song" value="Jingle Bells"/>
</bena>

byName自动装配遵循一项约定:为属性自动装配ID与该属性的名字相同的Bena。通过设置autowire属性为byName,Spring将特殊对待kenny的所有属性,为这些属性寻找与其名字相同的Spring Bean。在这里,Spring会发现instrument属性可以通过setter注入来进行自动装配。

使用byName自动装配的缺点是需要假设Bean的名字与其他Bean的属性的名字一样。

byType自动装配

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

constructor自动装配

如果要通过构造器注入来配置Bean,那我们可以移除<constructor-arg>元素,由Spring在应用上下文中自动选择Bean注入到构造器入参中。

例如:

<bean id="duke"
      class="com.springinaction.springidol.PoeticJuggler"
      autowire="constructor"
      />

上述声明告诉Spring去审视PoeticJuggler的构造器,并尝试在Spring配置中寻找匹配PoeticJuggler某一个构造器所有入参的Bean。

最佳自动装配

如果想自动装配Bean,但是又不能决定该使用那一种类型的自动装配。现在不必担心了,我们可以设置autuwire属性为autodetect,由Spring来决定。例如

<bean id="duke"
      class="com.springinaction.springidol.PoeticJuggle"
      autowire="autodetect"/>

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

默认自动装配

default-autowire

混合使用自动装配和显式装配

使用注解装配

从Spring2.5开始,最有趣的一种装配Spring Bean的方式是使用注解自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配并没有太大差别。但是使用注解方式允许更细粒度的自动装配,我们可以选择性地标注某一属性来对应其应用自动装配。

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

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

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

  • Spring自带的@Autowired注解
  • JSR-330的@Inject注解
  • JSR-250的@Resource注解

使用@Autowired

假设我们希望使用@Autowired让Spring自动装配乐器演奏家(Instrumentalist)Bean的instrument属性。则可以对setInstrument()方法进行标注,如下

@Autowired
public void setInstrument(Instrument instrument){
    this.instrument = instrument;
}

当Spring发现我们对setInstrument()方法使用了@Autowired注解时,Spring就会尝试对该方法执行byType自动装配。@Autowired也可以用于装配Bean的引用和标注构造器。

可选的自动装配

默认情况下,@Autowired具有强契约特征,其所标注的属性或参数必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性或参数中,自动装配就会失败(抛出令人讨厌的NoSuchBeanDefinitionException)。这可能是我们所期望的处理方式——当自动装配无法完成时,让Spring尽早失败,远胜于以后抛出异常。

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

@Autowired(required=false)
private Instrument instrument;

在这里,Spring将尝试装配instrument属性,但是如果没有查找到与之匹配的类型为Instrument的Bean,应用就不会发生任何问题,而instrument属性的值会设置为null。

限定歧义性的依赖

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

例如,为了确保Spirng为eddie Bean选择吉他(guitar)来演奏,即使有其他Bean也可以装配到instrument属性中,但我们可以使用@Qualifier来明确指定名为guitar的Bean:

@Autowired
@Qualifier("guitar")
private Instrument instrument;

如上所示,@Qualifier注解将尝试注入ID为guitar的Bean。

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

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

例如,我们有一个KnifeJuggler类需要注入一个或多个Knife的实例。假设Knife Bean的作用域声明为prototype,下面的KnifeJuggler的构造器将获得5个Knife Bean:

private Set<Knife> knives;

@Inject
public KnifeJuggler(Provider<Knife> knifeProvider){
    knives = new HashSet<Knife>();
    for(int i=0;i<5;i++){
        knives.add(knifeProvider.get());
    }
}

KnifeJuggler将获得一个Provider<Knife>,而不是在构造器中获得一个Knife实例。这个时候,只有provider被注入进去;在调用provider的get()方法之前,实际的Knife对象并没有被注入。在这个示例中,get()方法被调用了5次。因为Knife Bean的作用域为prototype,所以knife的Set集合将被赋予5个不同的Knife对象。

限定@Inject所标注的属性

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

@Inject
@Named("guitar")
private Instrument instrument;

在注解注入中使用表达式

@Value注解尽管易于使用,但我们很快就会发现,它同样具有威力。我们可以通过@Value直接标注某个属性、方法或者方法参数,并传入一个String类型的表达式来装配属性。例如:

@Value("Eruption")
private String song;

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

自动检测Bean

<context:component-scan>元素除了完成了与<context:annotation-config>一样的工作,还允许Spring自动检测Bean和定义Bean。

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

为自动检测标注Bean

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

  • @Component ——通用的构造型注解,标识该类为Spring组件。
  • @Controller——标识将该类定义为SpringMVC controller。
  • @Repository——标识将该类定义为数据仓库
  • @Service——标识将该类定义为服务。
  • 使用@Component标注的任意自定义注解。

过滤组件扫描

通过为<context:component-scan>配置<context:include-filter>和/或者<context:exclude-filter>子元素,我们可以随意调整扫描行为。

过滤器类型 描述
annotation 过滤器扫描使用指定注解所标注的那些类,通过expression属性指定要扫描的注解
assignable 过滤器扫描派生于expression属性所指定类型的那些类
aspectj 过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类
custom 使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression
regex 过滤器扫描类的名称与expression属性所指定的正则表达式匹配的那些类
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容