【Spring】装配Bean-(1)-使用XML配置


从Spring3.0开始,Spring容器提供了两种配置Bean的方式:

Spring Bean配置 - 使用XML

使用一个或多个XML文件作为配置文件

Spring配置文件的根元素是<beans>。

Spring的核心框架自带了10个命名空间配置:

【Spring】装配Bean-(1)-使用XML配置_第1张图片

Spring框架的其他模块也提供了自己的命名空间。


<bean>元素是Spring中最简单的配置单元,通过该元素Spring将创建一个对象。当Spring容器加载该Bean时,Spring将使用默认的构造函数来实例化Bean

基于构造函数注入

使用<constructor-arg>元素。如果不配置<constructor-arg>元素,那么Spring将使用默认的构造函数。

<bean id="poeticDuke" 
      class="com.springinaction.springidol.PoeticJuggler"> 
    <constructor-arg value="15" /> 
    <constructor-arg ref="otherbeanid" /> 
</bean>

通过工厂方法创建Bean

有时候静态工厂方法是实例化对象的唯一方法。Spring通过<bean>元素的factory-method属性来配置使用工厂创建的Bean。

如下的单例类:

package com.springinaction.springidol;  
public class Stage {  
    private Stage() {  
    }  
    
    private static class StageSingletonHolder {  
        static Stage instance = new Stage();  
    }  
    
    public static Stage getInstance() {  
        return StageSingletonHolder.instance;  
    }  
} 

<bean>元素有一个factory-method属性,允许我们调用一个指定的静态方法,从而代替构造函数来创建一个类的实例:

<bean id="theStage" 
      class="com.springinaction.springidol.Stage" 
      factory-method="getInstance" /> 

这里的单例类是设计模式中的类,不是Spring中单例(作用域)Bean。

配置Bean的作用域

所有的Spring Bean默认都是单例。当容器分配一个Bean时,它总是返回Bean的同一个实例。

当在Spring中配置<bean>元素时,可以为Bean声明一个作用域。配置Bean的scope属性, 例如:

<bean id="beanname" class="com.springsample.TestClass" scope="prototype"/>

Spring提供了几个作用域选项:(默认是singleton)

【Spring】装配Bean-(1)-使用XML配置_第2张图片

Spring单例的概念仅限于Spring上下文的范围内。Spring的单例Bean只能保证在每个应用上下文中只有一个Bean的实例。还是可以使用传统的方式实例化同一个Bean。

配置Bean的初始化和销毁方法

Spring提供了Bean生命周期的钩子方法。

为Bean定义初始化和销毁操作,只需要使用init-method和destroy-method参数来配置<bean>元素。init-method属性指定了在初始化Bean时要调用的方法;destroy-method属性指定了Bean从容器移除之前要调用的方法。

示例:

public class Auditorium {  
    public void turnOnLights() {  
        ..  
    }  
    public void turnOffLights() {  
        ..  
    }  
} 

<bean id="auditorium" 
      class="com.springinaction.springidol.Auditorium" 
      init-method="turnOnLights" 
      destroy-method="turnOffLights"/> 

使用<beans>元素的default-init-method和default-destroy-method属性配置默认的初始化方法和销毁方法:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"  
       default-init-method="turnOnLights" 
       default-destroy-method="turnOffLights"> ...  
</beans> 

为Bean定义初始化和销毁方法的另一种可选方法是:让Bean实现Spring的InitializingBean和DisposableBean接口。

注入Bean的属性/成员变量

通常JavaBean的属性是私有的,同时拥有一组setXXX和getXXX方法。Spring可以借助属性的setXXX方法来配置属性的值。

示例:

package com.springinaction.springidol;  

public class Instrumentalist implements Performer {  
    public Instrumentalist() {  
    }    
    
    private String song;  
    public void setSong(String song) {  
        this.song = song;  
    }  
 
    private Instrument instrument;  
    public void setInstrument(Instrument instrument) {  
        this.instrument = instrument;  
    }  
} 

  • 注入简单值

在Spring中可以使用<property>元素配置Bean的属性。<property>在许多方面都与<constructor-arg>类似,不过是一个是通过构造函数来注入值,另一个是通过调用属性的setXXX方法来注入值。

<bean id="kenny" 
      class="com.springinaction.springidol.Instrumentalist"> 
    <property name="song" value="Jingle Bells" /> 
</bean> 

一旦Bean被实例化,Spring就会调用<property>元素所指定属性的setXXX方法为该属性注入值。

Spring将根据Bean属性的类型自动判断value值的正确类型。

  • 引用其他的Bean

示例:

package com.springinaction.springidol;  
public class Saxophone implements Instrument {  
    public Saxophone() {  
    }   
}

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

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

如果使用命名空间p,只需要在Spring的XML配置中增加一段声明:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:p="http://www.springframework.org/schema/p" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 

使用 p:作为<bean>元素所有属性的前缀来装配Bean的属性:

<bean id="kenny" class="com.springinaction.springidol.Instrumentalist" 
      p:song = "Jingle Bells" 
      p:instrument-ref = "saxophone" /> 

-ref后缀作为一个标识来告知Spring应该装配一个引用而不是字面值。

  • 装配集合属性

当Bean的属性值的类型是集合类,Spring提供了4中类型的集合配置元素:

【Spring】装配Bean-(1)-使用XML配置_第3张图片

当装配属性类型为数组或者java.util.Collection时,使用<list>和<set>元素;

当类型为为java.util.Map和java.util.Properties时,使用<map>和<props>元素;

<map>和<props>的区别是,<props>要求键和值都必须为String类型,<map>允许键和值可以是任意类型的;

  • 装配空值属性

示例:

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

内部Bean

内部Bean是定义在其他Bean内部的Bean.

示例:

<bean id="kenny" 
      class="com.springinaction.springidol.Instrumentalist"> 
    <property name="song" value="Jingle Bells" /> 
    <property name="instrument"> 
        <bean class="org.springinaction.springidol.Saxophone" /> 
    </property> 
</bean> 

内部Bean并不仅限于setXXX注入,还可以把内部Bean装配到构造方法的参数中。示例:

<bean id="duke" 
      class="com.springinaction.springidol.PoeticJuggler"> 
    <constructor-arg value="15" /> 
    <constructor-arg> 
        <bean class="com.springinaction.springidol.Sonnet29" /> 
    </constructor-arg> 
</bean> 

内部Bean没有ID属性,我们不会通过名字来引用内部Bean。内部Bean不能被复用,仅适用于一次性注入,而且也不能被其他Bean所引用。

使用SpEL表达式配置Bean

Spring3.0引入了Spring表达式语言(Spring Expression Language, SpEL)。SpEL是一种装配Bean的方式,它通过运行期执行的表达式将值装配到Bean的属性或构造器参数中。


自动装配属性

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

byName自动装配

通过设置autowire属性为byName,Spring将特殊队列Bean的所有属性,为这些属性寻找与其名字相同的Spring Bean.

示例:显式配置Bean的instrument属性:

<bean id="kenny2"
      class="com.springinaction.springidol.Instrumentalist">
    <property name="song" value="Jingle Bells" />
    <property name="instrument" ref="saxophone" />
</bean>
假设有如下的Bean:

<bean id="instrument"
      class="com.springinaction.springidol.Saxophone" />

Saxophone Bean的id属性与kenny2 Bean的instrument属性的名字是一样的。通过配置autowire属性,为属性自动装配ID与属性的名字相同的Bean:

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

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

byType自动装配

byType检查属性的类型。当我们尝试使用byType自动装配时,Spring会寻找哪一个Bean的类型与属性的类型相匹配。

上例中kenny Bean的autowire属性设置为byType,Spring容器会查找哪一个Bean的类型与Instrument类型相匹配。如果匹配,则把该Bean装配到kenny的instrument属性中。

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

为了避免因为使用byType自动装配而带来的歧义,Spring提供了两个选择:

  • 为自动装配标识一个首选的Bean;

使用<bean>元素的primary属性,如果只有一个自动装配的候选Bean的primary属性设置为true,那么该Bean将比其他候选Bean优先被选择。但primary属性的默认设置为true,这意味着所有的候选Bean都将变成首选Bean。所以,为了使用primary属性,不得不将所有非首选Bean的primary属性设置为false。

<bean id="saxophone"
      class="com.springinaction.springidol.Saxophone" 
      primary="false" />

  • 可以取消某个Bean自动装配的候选资格;

如果在自动装配时,希望排除某些Bean,那么可以设置这些Bean的autowire-candidate属性为false:

<bean id="saxophone"
      class="com.springinaction.springidol.Saxophone" 
      autowire-candidate="false" />

Spring在自动装配时忽略saxophone Bean作为候选Bean。

constructor自动装配

如果需要通过构造器注入来配置Bean,那么可以移除<constructor-arg>元素,由Spring在应用上下文中子的哦那个选择Bean注入到构造器参数中,示例:

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

constructor自动装配具有和byType自动装配相同的局限性。

最佳自动装配

设置autowire属性为autodetect:

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

Spring将首先尝试使用constructor自动装配,如果没有发现与构造器相匹配的Bean时,Spring将尝试使用byType自动装配。

默认自动装配

如果需要为Spring应用上下文中的每一个Bean配置相同的autowire属性,那么就可以要求Spring为它所创建的所有Bean应用相同的自动装配策略来简化配置。在根元素<beans>上增加一个default-autowire属性:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"  
       default-autowire="byType"> ...  
</beans> 

默认情况下,default-autowire属性被设置为none,标识所有Bean都不使用自动装配,除非Bean自动配置了autowire属性。

default-autowire只应用于指定Spring配置文件中的所有Bean,可以在一个Spring应用上下文中定义多个配置文件,每一个配置文件都可以有自己的默认自动装配策略。

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

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

对某个Bean选择了自动装配策略,并不代表不能对该Bean的某些属性进行显式装配。仍让可以为任意一个属性配置<property>元素。

使用混合装配的最后一个注意事项:当使用constructor自动装配策略时,我们必须让Spring自动装配构造器的所有入参——不能混合使用constructor自动装配策略和<constructor-arg>元素。

使用注解装配属性

使用注解自动装配与在XML中使用autowire属性自动装配并没有太大差别。使用注解方式允许更细粒度的自动装配,可以选择性地标注某一个属性来对其应用自动装配。

Spring容器默认禁用注解装配

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

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context
                           http://www/springframework.org/schema/context/spring-context-3.0.xsd"> 
    <context:annotation-config />
    ... ...
</beans>

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

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

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


自动检测Bean

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

为了配置Spring自动检测,需要使用<context:component-scan>元素来代替<context:annotation-config>元素:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context
                           http://www/springframework.org/schema/context/spring-context-3.0.xsd"> 
    <context:component-scan
        base-package="com.springinaction.springidol" >
    </context:component-scan>
    ... ...
</beans> 

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

为自动检测标注Bean

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

  • @Component - 通用的构造器注解,标识该类为Spring组件;
  • @Controller - 标识该类定义为Spring MVC controller;
  • @Respository - 标识该类定义为数据仓库;
  • @Service - 标识将该类定义为服务;
  • 使用@Component标注的任意自定义注解;

示例:

假设应用上下文中仅仅包含eddie和guitar两个Bean。可以配置<context:component-scan>元素并使用@Component注解标识Instrumenalist和Guitar类,从而消除显式的<bean>定义。

package com.springinaction.springidol;

import org.springframework.stereotype.Component;

@Component
public class Guitar implements Instrument{
	。。。 。。。
}

Bean的ID默认为无限定类名,guitar。

package com.springinaction.springidol;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("eddie")
public class Instrumentalist implements Performer{
	。。。 。。。
}

指定一个Bean ID作为@Component注解的参数,该Bean ID是显式命名为eddie。

过滤组件扫描

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


Spring Bean配置 - 基于Java的配置

创建基于Java的配置

仍然需要极少量的XML来启用Java配置:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context
                           http://www/springframework.org/schema/context/spring-context-3.0.xsd"> 
    <context:component-scan
        base-package="com.springinaction.springidol" />
    ... ...
</beans>

<context:component-scan>也会自动加载使用@Configuration注解所标注的类。base-package属性告知Spring在com.springinaction.springidol包内查找使用@Configuration注解所标注的所有类。

在基于Java的配置里使用@Configuration注解的Java类,就等价于XML配置中的<beans>元素

package com.springinaction.springidol;

import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringIdolConfig{
  // Bean declaration methods go here
}

这个类将包含一个或多个Spring Bean的定义。这些Bean的定义是使用@Bean注解所标注的方法。

@Bean
public Performer duke() {
  return new Juggle();
}

这个简单方法就是Java配置,它等价于我们之前使用XML所配置的<bean>元素。@Bean告知Spring这个方法将返回一个对象,该对象应该被注册为Spring应用上下文中的一个Bean。方法名将作为该Bean的ID。

为Bean装配另一个Bean的应用:

首先声明一个sonnet29 Bean:

@Bean
private Poem sonnet29() {
  return new Sonnet29();
}

再创建一个PoeticJuggle Bean,通过构造器为它装配sonnet29 Bean:

@Bean
public Performer poeticDuke() {
  return new PoeticJuggler(sonnet29()); 
}

在Spring的Java配置中,通过声明方法引用一个Bean并不等同于调用该方法,否则每次调用sonnet29()都将得到该Bean的一个新的实例。通过使用@Bean注解标注sonnet29()方法,会告知Spring我们希望该方法定义的Bean要被注册进Spring的应用上下文中。因此,在其他Bean的声明方法中引用这个方法时,Spring都会拦截该方法的调用,并尝试在应用上下文中查找该Bean,而不是让方法创建一个新的实例。



你可能感兴趣的:(spring)