likes
comments
collection
share

Spring 依赖配置方式详解-基于Xml配置元数据

作者站长头像
站长
· 阅读数 3

Bean的注入方式概览

在Spring中Bean的声明方式可以分为三类,构造函数、工厂方法、setter方法进行声明,其中构造函数的形式是Spring最推荐的形式。工厂方法其实也可以算作构造函数的一种表现形式,工厂方法又可分为两种,一种是静态工厂方法声明,另一种就是实例工厂方法声明。setter方式的声明,是针对于类中的属性的,因此,如果一个类中的某个属性如果没有在声明bean的文件中声明,那么这个属性的值可能为空,简而言之,setter方式的声明是不做强校验的,是可以为空的。

构造函数形式

  • 使用默认构造函数注入Bean

    <bean id="beanTwo" class="com.study.spring.ioc.testbean.ThingTwo"/>
    <bean id="beanThree" class="com.study.spring.ioc.testbean.ThingThree"/>
    
  • 使用参数的构造函数注入Bean

    1. 通过Spring容器进行类型自动推断注入

      <bean id="beanOne" class="com.study.spring.ioc.testbean.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
      </bean>
      
    2. 指定参数类型注入

      <bean id="constructorArgsTypeTestBean" class="com.study.spring.ioc.testbean.ConstructorArgsTypeTestBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
      </bean>
      
    3. 指定参数顺序注入

      <bean id="constructorArgsIndexTestBean" class="com.study.spring.ioc.testbean.ConstructorArgsIndexTestBean">
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/>
      </bean>
      
    4. 指定参数名称注入

      <bean id="constructorArgsNameTestBean" class="com.study.spring.ioc.testbean.ConstructorArgsNameTestBean">
        <constructor-arg name="years" value="7500000"/>
        <constructor-arg name="ultimateAnswer" value="42"/>
      </bean>
      

工厂方法形式

  • 静态工厂方法注入

    在私有构造函数,通过静态工厂实例对象时,设置factory-method属性的值为静态工厂方法的名称

    1. 无参注入

      <bean id="staticFactoryClientService" class="com.study.spring.ioc.services.StaticFactoryClientService" 
            factory-method="createInstance"/>
      
    2. 有参注入

      <bean id="anotherBean" class="com.study.spring.ioc.testbean.AnotherBean"/>
      <bean id="yetAnotherBean" class="com.study.spring.ioc.testbean.YetAnotherBean"/>
      <bean id="staticFactoryMethodTransitiveDependency"
            class="com.study.spring.ioc.testbean.StaticFactoryMethodTransitiveDependency" factory-method="createInstance">
        <constructor-arg ref="anotherBean"/>
        <constructor-arg ref="yetAnotherBean"/>
        <constructor-arg value="1"/>
      </bean>
      
  • 实例工厂方法注入

    定义一个实例工厂的Bean,通过工厂Bean中的方法示例化普通Bean

    1. 无参注入

      <bean id="serviceLocator" class="com.study.spring.ioc.locator.DefaultServiceLocator"/>
      <bean id="instanceFactoryClientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
      <bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/>
      
    2. 有参注入

      <bean id="serviceLocator" class="com.study.spring.ioc.locator.DefaultServiceLocator"/>
      <bean id="instanceFactoryMethodTransitiveDependency" factory-bean="serviceLocator"
            factory-method="createTransitiveDependency">
        <constructor-arg ref="anotherBean"/>
        <constructor-arg ref="yetAnotherBean"/>
        <constructor-arg value="1"/>
      </bean>
      

settter方法形式

<bean id="setterInjectTestBean" class="com.study.spring.ioc.testbean.SetterInjectTestBean">
  <property name="anotherBean" ref="anotherBean"/>
  <property name="yetAnotherBean" ref="yetAnotherBean"/>
  <property name="integerProperty" value="1"/>
</bean>

Bean 依赖配置详解

在上一部分已描述了 Bean 的几种注入方式,接下来,这一部分将会基于setter方式注入这种方式来讲述一下声明时的元数据的一些元素是做什么用的。

因为property结点和constructor-arg结点这两种的子元素几乎是相似的,用法含义几乎一致,所以,这里用property来作为那些子元素的父元素。

Idref

idref 元素是将容器中另一个 Bean 的 id(字符串值,而不是引用)传递给 <constructor-arg/> <property/> 元素的一种简单的防错方法。简单来说,就是将其它已定义Bean的id值映射给当前Bean的属性。

<bean id="idrefTestBean" class="com.study.spring.ioc.testbean.IdrefTestBean">
  <property name="beanOne">
    <idref bean="anotherBean"/>
  </property>
  <property name="beanTwo">
    <idref bean="yetAnotherBean"/>
  </property>
</bean>
public class IdrefTestBean {

    private String beanOne;
    private String beanTwo;
    private int beanThree;

    public void setBeanOne(String beanOne) {
        this.beanOne = beanOne; // beanOne = "anotherBean"
    }

    public void setBeanTwo(String beanTwo) {
        this.beanTwo = beanTwo; // beanTwo = "yetAnotherBean"
    }

    public void setBeanThree(int beanThree) {
        this.beanThree = beanThree;
    }
}

Ref

ref元素将某个 Bean 的指定属性值设置为对容器管理的另一个 Bean的引用。被引用的 Bean 是要设置其属性的 Bean的依赖关系,它会在设置属性前根据需要被初始化。所有引用最终都是对另一个对象的引用,简单来说就是引用其它Bean然后设置到当前Bean的属性里。

<bean id="setterInjectTestBean2" class="com.study.spring.ioc.testbean.SetterInjectTestBean">
  <property name="anotherBean">
    <ref bean="anotherBean"/>
  </property>
  <property name="yetAnotherBean">
    <ref bean="yetAnotherBean"/>
  </property>
  <property name="integerProperty">
    <value>1</value>
  </property>
</bean>

从代码上看,它的效果和<property name="anotherBean" ref="anotherBean"/>是一样的

Inner Beans

<property/><constructor-arg/>元素内可以通过<bean/> 元素定义一个内部 Bean。内部 bean 定义不需要定义 ID或名称。如果指定了,容器不会使用这样的值作为标识符。容器还会忽略创建时的作用域标记,因为内部 Bean 总是匿名的,而且总是与外部 Bean 一起创建。除了注入到外层 Bean 中,无法独立访问内部 Bean 或将其注入到协作 Bean 中。

<bean id="setterInjectTestBean3" class="com.study.spring.ioc.testbean.SetterInjectTestBean">
  <property name="anotherBean">
    <bean class="com.study.spring.ioc.testbean.AnotherBean"/>
  </property>
  <property name="yetAnotherBean">
    <bean id="yetAnotherBean" class="com.study.spring.ioc.testbean.YetAnotherBean"/>
  </property>
  <property name="integerProperty">
    <value>1</value>
  </property>
</bean>

Collections

<list/><set/><map/><props/> 元素分别设置了 Java 集合类型 List、Set、Map 和 Properties的属性和参数。

而这些元素里包含的内容与<Property />一样,也包括以下内容:

bean | ref | idref | list | set | map | props | value | null

下面是定义一个Bean中如果存在上述的Java类型时的定义代码:

<bean id="moreComplexObject" class="com.study.spring.ioc.testbean.ComplexObject">
  <!--  java.util.Properties  -->
  <property name="adminEmails">
    <props>
      <prop key="administrator">administrator@email.com</prop>
      <prop key="support">support@email.com</prop>
      <prop key="development">development@email.com</prop>
    </props>
  </property>
  <!--  java.util.List  -->
  <property name="someList">
    <list>
      <value>a list element followed by a reference</value>
      <ref bean="anotherBean"/>
    </list>
  </property>
  <!--  java.util.Map  -->
  <property name="someMap">
    <map>
      <entry key="an entry" value="just some string"/>
      <entry key="a ref" value-ref="anotherBean"/>
    </map>
  </property>
  <!--  java.util.Set  -->
  <property name="someSet">
    <set>
      <value>just some string</value>
      <ref bean="anotherBean"/>
    </set>
  </property>
</bean>
public class ComplexObject {

    private Properties adminEmails;
    private List<Object> someList;
    private Map<String, Object> someMap;
    private Set<Object> someSet;

    // ... 省略 getter 和 setter 方法
}

Collection Merging

在 Spring 中提供了父子bean的定义方式,子bean可通过parent属性指定父Bean,如果父Bean中已经定义了一些属性,子Bean可以直接使用父Bean中的属性值,也可以将父Bean中的属性值进行覆盖,也可以添加一些新的属性值。在父Bean存在集合类的属性时,除了这几种情况,也可以将子bean和父Bean的集合属性值进行合并。合并的方式就是在<list/><set/><map/><props/> 等集合元素中添加merge=true这个属性。需要注意的是,因为List是一个有序集合,因此为了保持有序集合的概念,父列表的值将优先于子列表的值,其它几种集合不存在排序语义。

配置父 Bean

<bean id="parent" abstract="true" class="com.study.spring.ioc.testbean.ComplexObject">
  <property name="adminEmails">
    <props>
      <prop key="administrator">administrator@email.com</prop>
      <prop key="support">support@email.com</prop>
    </props>
  </property>
  <property name="someList">
    <list>
      <value type="java.lang.String">this is parent</value>
    </list>
  </property>
  <property name="someMap">
    <map merge="true">
      <entry key="p" value="parent"/>
      <entry key="p-ref" value-ref="anotherBean"/>
    </map>
  </property>
</bean>

配置子 Bean

<bean id="child" parent="parent">
  <property name="adminEmails">
    <!-- merge 属性在子 Bean 的集合属性中开启,子 Bean 的集合属性即会合并父 Bean 的该属性 -->
    <props merge="true">
      <prop key="sales">sales@email.com</prop>
      <!-- 该属性的key和 父 bean 中定义的一样,因此会覆盖父 Bean 中的值-->
      <prop key="support">support@email.cn</prop>
    </props>
  </property>
  <property name="someList">
    <list merge="true">
      <value type="java.lang.String">this is child</value>
    </list>
  </property>
  <property name="someMap">
    <map merge="true">
      <entry key="c" value="child"/>
      <entry key="c-ref" value-ref="yetAnotherBean"/>
    </map>
  </property>
</bean>

指定集合类型

Spring将强类型集合依赖注入到Bean时,会将强类型集合实例的元素添加到集合之前将其转换为相应的类型。

public class SomeClass {

	private Map<String, Float> accounts;

	public void setAccounts(Map<String, Float> accounts) {
		this.accounts = accounts;
	}
}
<beans>
  <bean id="something" class="x.y.SomeClass">
    <property name="accounts">
      <map>
        <entry key="one" value="9.99"/>
        <entry key="two" value="2.75"/>
        <entry key="six" value="3.99"/>
      </map>
    </property>
  </bean>
</beans>

null 和 空字符串

  • 空字符串 当声明Bean时,<property/>value属性设置为空字符串时,Bean在实例化后改属性的值就是空字符串

    <bean class="ExampleBean">
      <property name="email" value=""/>
    </bean>
    
  • null

    如果在声明Bean需要将属性设为null值时,可以在<property/>元素中使用<null/>元素声明

    <bean class="ExampleBean">
    	<property name="email">
    		<null/>
    	</property>
    </bean>
    

简写Bean配置

在Bean的声明文件中,可以使用namespace的配置来简写<property /><constructor-arg />元素,可已在Bean上通过属性进行配置。

使用p-namespace

在Bean的声明文件中,配置p-namespace,使<bean/>元素通过属性来描述Bean,省略了<property/>元素。具体的配置就是在<beans/>元素中添加xmlns:p="http://www.springframework.org/schema/p"属性。之后再<bean/>元素中就可以通过p:属性=""的形式进行配置了。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- 使用 property 元素配置 -->
	<bean name="john-classic" class="com.example.Person">
		<property name="name" value="John Doe"/>
		<property name="spouse" ref="jane"/>
	</bean>

  <!-- 使用 p-namespace Bean 属性配置 -->
	<bean name="john-modern"
		class="com.example.Person"
		p:name="John Doe"
		p:spouse-ref="jane"/>

	<bean name="jane" class="com.example.Person">
		<property name="name" value="Jane Doe"/>
	</bean>
</beans>

使用c-namespace

与使用p-namespace来省略<property />元素一致,可以使用c-namespace来省略<constructor-arg />元素。配置方式也和p-namespace类似,在<beans/>元素中添加xmlns:c="http://www.springframework.org/schema/c"属性。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:c="http://www.springframework.org/schema/c"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="beanTwo" class="x.y.ThingTwo"/>
	<bean id="beanThree" class="x.y.ThingThree"/>

	<!-- traditional declaration with optional argument names -->
	<bean id="beanOne" class="x.y.ThingOne">
		<constructor-arg name="thingTwo" ref="beanTwo"/>
		<constructor-arg name="thingThree" ref="beanThree"/>
		<constructor-arg name="email" value="something@somewhere.com"/>
	</bean>

	<!-- c-namespace declaration with argument names -->
	<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
		c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

depends-on

dependes-on<bean/>元素的一个属性,它显示的声明了该Bean所需的依赖Bean,Spring会在初始化这个Bean之前初始化depends-on中声明的Bean。通常情况下可以用<ref/>元素代替,但当Bean的依赖关系不是很直接时,就需要显示声明。例如,需要触发一个类中的静态初始化器,如数据库驱动程序注册。depends-on 属性可以显式地强制在使用此元素的 Bean 初始化之前初始化一个或多个 Bean。

dependes-on属性可以配置多个Bean,可以用逗号、空格、分号隔开各个Bean。

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
  <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

Bean 的懒加载

在默认情况下,ApplicationContext的实现会创建和配置所有的单例Bean,作为初始化的一部分。这样的好处就是,如果Bean的配置存在问题,会立即被发现。但如果有特殊情况,需要懒加载Bean时,可以在配置元数据文件中的<bean/>元素中添加lazy-init="true"l来开启懒加载。但如果,已被声明为懒加载的单例Bean被其他没有声明为懒加载的Bean引用时,则被声明为懒加载的Bean则会在初始化时就被配置,懒加载的配置就不启作用了。

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>

Bean 的自动装配

Spring容器可以自动装配Bean的依赖。简单来说就是通过Spring来帮助你完成每个Bean的依赖注入,大大减少了配置的繁琐,以及后期对每个Bean属性的维护成本。在基于Xml的配置文件中,使用<bean/>元素的autowire属性来配置自动装配。自动装配有四种模式:

  • no模式,是自动装配的默认模式,表示不启用自动装配。

  • byName模式,根据bean的属性名来查找相同名称的bean,是根据setter方法进行依赖注入的。

    <bean id="autowiredByNameBean" class="com.study.spring.ioc.testbean.AutowiredByNameBean" autowire="byName"/>
    
  • byType模式,根据bean属性的类型查找该类型的bean,如果存在多个相同类型的bean,且当前这个bean的该属性不是集合属性,就会报错。也就是说当前Bean待注入的属性如果是一个集合,那么就会去匹配该集合类型的所有的Bean。

    • 当前属性的类型不是集合类型并且在容器中只有一个bean

      <bean id="autowiredByTypeBean" class="com.study.spring.ioc.testbean.AutowiredByTypeBean" autowire="byType"/>
      
      public class AutowiredByTypeBean {
      
          private AnotherBean anotherBean;
          private YetAnotherBean yetAnotherBean;
      
          public void setAnotherBean(AnotherBean anotherBean) {
              this.anotherBean = anotherBean;
          }
      
          public void setYetAnotherBean(YetAnotherBean yetAnotherBean) {
              this.yetAnotherBean = yetAnotherBean;
          }
      }
      
    • 当前属性是一个集合类型,并且容器中有多个bean

      <bean id="anotherBean1" class="com.study.spring.ioc.testbean.AnotherBean"/>
      <bean id="anotherBean2" class="com.study.spring.ioc.testbean.AnotherBean"/>
      <bean id="autowiredListPropertyByTypeBean" class="com.study.spring.ioc.testbean.AutowiredListPropertyByTypeBean"
            autowire="byType"/>
      
      public class AutowiredListPropertyByTypeBean {
      
          private List<AnotherBean> anotherBeans;
      
          public void setAnotherBeans(List<AnotherBean> anotherBeans) {
              this.anotherBeans = anotherBeans;
          }
      
          public List<AnotherBean> getAnotherBeans() {
              return anotherBeans;
          }
      }
      
  • constructor模式,和byType模式类似,不过类型不是属性类型,而是构造参数类型。

    <bean id="autowiredByConstructor" class="com.study.spring.ioc.testbean.AutowiredByConstructorBean"
          autowire="constructor"/>
    
    public class AutowiredByConstructorBean {
    
        private final AnotherBean anotherBean;
        private final YetAnotherBean yetAnotherBean;
    
        public AutowiredByConstructorBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean) {
            this.anotherBean = anotherBean;
            this.yetAnotherBean = yetAnotherBean;
        }
    }
    

    上述几种模式中,使用byType模式时,就需要注意一个类型有没有在容器中定义了多个bean,或者该属性是不是一个集合。除此之外,自动装配不能用于装配原始类型以及简单属性的数组。当容器中存在同一个类型的多个bean时,可以使用以下几种方法解决报错:

    • 放弃自动装配,改用显示的形式配置依赖(ref)

    • 在统一类型的bean中,选择一个作为主要的bean,给这个bean配置primary属性。Spring在加载时就会优先选择这个bean

      <bean id="anotherBean1" primary="true" class="com.study.spring.ioc.testbean.AnotherBean"/>
      
    • 从自动装配中排除多余的bean,在<bean/>与元素中配置autowire-condidate属性为false,该多余的bean就会被排除在自动装配外。除此之外,也可以在顶层的<beans/>元素中的default-autowire-candidates设置自动装配的匹配原则,例如,要将自动连接候选状态限制为名称以 Repository 结尾的任何 Bean,可提供 *Repository 值。要提供多个模式,请用逗号分隔的列表定义它们。Bean 定义的 autowire-candidate 属性的显式值 true 或 false 始终优先。

在Spring中Bean的声明方式可以分为三类,构造函数、工厂方法、setter方法进行声明,其中构造函数的形式是Spring最推荐的形式。工厂方法其实也可以算作构造函数的一种表现形式,工厂方法又可分为两种,一种是静态工厂方法声明,另一种就是实例工厂方法声明。setter方式的声明,是针对于类中的属性的,因此,如果一个类中的某个属性如果没有在声明bean的文件中声明,那么这个属性的值可能为空,简而言之,setter方式的声明是不做强校验的,是可以为空的。

构造函数形式

  • 使用默认构造函数注入Bean

    <bean id="beanTwo" class="com.study.spring.ioc.testbean.ThingTwo"/>
    <bean id="beanThree" class="com.study.spring.ioc.testbean.ThingThree"/>
    
  • 使用参数的构造函数注入Bean

    1. 通过Spring容器进行类型自动推断注入

      <bean id="beanOne" class="com.study.spring.ioc.testbean.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
      </bean>
      
    2. 指定参数类型注入

      <bean id="constructorArgsTypeTestBean" class="com.study.spring.ioc.testbean.ConstructorArgsTypeTestBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
      </bean>
      
    3. 指定参数顺序注入

      <bean id="constructorArgsIndexTestBean" class="com.study.spring.ioc.testbean.ConstructorArgsIndexTestBean">
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/>
      </bean>
      
    4. 指定参数名称注入

      <bean id="constructorArgsNameTestBean" class="com.study.spring.ioc.testbean.ConstructorArgsNameTestBean">
        <constructor-arg name="years" value="7500000"/>
        <constructor-arg name="ultimateAnswer" value="42"/>
      </bean>
      

工厂方法形式

  • 静态工厂方法注入

    在私有构造函数,通过静态工厂实例对象时,设置factory-method属性的值为静态工厂方法的名称

    1. 无参注入

      <bean id="staticFactoryClientService" class="com.study.spring.ioc.services.StaticFactoryClientService" 
            factory-method="createInstance"/>
      
    2. 有参注入

      <bean id="anotherBean" class="com.study.spring.ioc.testbean.AnotherBean"/>
      <bean id="yetAnotherBean" class="com.study.spring.ioc.testbean.YetAnotherBean"/>
      <bean id="staticFactoryMethodTransitiveDependency"
            class="com.study.spring.ioc.testbean.StaticFactoryMethodTransitiveDependency" factory-method="createInstance">
        <constructor-arg ref="anotherBean"/>
        <constructor-arg ref="yetAnotherBean"/>
        <constructor-arg value="1"/>
      </bean>
      
  • 实例工厂方法注入

    定义一个实例工厂的Bean,通过工厂Bean中的方法示例化普通Bean

    1. 无参注入

      <bean id="serviceLocator" class="com.study.spring.ioc.locator.DefaultServiceLocator"/>
      <bean id="instanceFactoryClientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
      <bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/>
      
    2. 有参注入

      <bean id="serviceLocator" class="com.study.spring.ioc.locator.DefaultServiceLocator"/>
      <bean id="instanceFactoryMethodTransitiveDependency" factory-bean="serviceLocator"
            factory-method="createTransitiveDependency">
        <constructor-arg ref="anotherBean"/>
        <constructor-arg ref="yetAnotherBean"/>
        <constructor-arg value="1"/>
      </bean>
      

settter方法形式

<bean id="setterInjectTestBean" class="com.study.spring.ioc.testbean.SetterInjectTestBean">
  <property name="anotherBean" ref="anotherBean"/>
  <property name="yetAnotherBean" ref="yetAnotherBean"/>
  <property name="integerProperty" value="1"/>
</bean>

Bean 依赖配置详解

在上一部分已描述了 Bean 的几种注入方式,接下来,这一部分将会基于setter方式注入这种方式来讲述一下声明时的元数据的一些元素是做什么用的。

因为property结点和constructor-arg结点这两种的子元素几乎是相似的,用法含义几乎一致,所以,这里用property来作为那些子元素的父元素。

Idref

idref 元素是将容器中另一个 Bean 的 id(字符串值,而不是引用)传递给 <constructor-arg/> <property/> 元素的一种简单的防错方法。简单来说,就是将其它已定义Bean的id值映射给当前Bean的属性。

<bean id="idrefTestBean" class="com.study.spring.ioc.testbean.IdrefTestBean">
  <property name="beanOne">
    <idref bean="anotherBean"/>
  </property>
  <property name="beanTwo">
    <idref bean="yetAnotherBean"/>
  </property>
</bean>
public class IdrefTestBean {

    private String beanOne;
    private String beanTwo;
    private int beanThree;

    public void setBeanOne(String beanOne) {
        this.beanOne = beanOne; // beanOne = "anotherBean"
    }

    public void setBeanTwo(String beanTwo) {
        this.beanTwo = beanTwo; // beanTwo = "yetAnotherBean"
    }

    public void setBeanThree(int beanThree) {
        this.beanThree = beanThree;
    }
}

Ref

ref元素将某个 Bean 的指定属性值设置为对容器管理的另一个 Bean的引用。被引用的 Bean 是要设置其属性的 Bean的依赖关系,它会在设置属性前根据需要被初始化。所有引用最终都是对另一个对象的引用,简单来说就是引用其它Bean然后设置到当前Bean的属性里。

<bean id="setterInjectTestBean2" class="com.study.spring.ioc.testbean.SetterInjectTestBean">
  <property name="anotherBean">
    <ref bean="anotherBean"/>
  </property>
  <property name="yetAnotherBean">
    <ref bean="yetAnotherBean"/>
  </property>
  <property name="integerProperty">
    <value>1</value>
  </property>
</bean>

从代码上看,它的效果和<property name="anotherBean" ref="anotherBean"/>是一样的

Inner Beans

<property/><constructor-arg/>元素内可以通过<bean/> 元素定义一个内部 Bean。内部 bean 定义不需要定义 ID或名称。如果指定了,容器不会使用这样的值作为标识符。容器还会忽略创建时的作用域标记,因为内部 Bean 总是匿名的,而且总是与外部 Bean 一起创建。除了注入到外层 Bean 中,无法独立访问内部 Bean 或将其注入到协作 Bean 中。

<bean id="setterInjectTestBean3" class="com.study.spring.ioc.testbean.SetterInjectTestBean">
  <property name="anotherBean">
    <bean class="com.study.spring.ioc.testbean.AnotherBean"/>
  </property>
  <property name="yetAnotherBean">
    <bean id="yetAnotherBean" class="com.study.spring.ioc.testbean.YetAnotherBean"/>
  </property>
  <property name="integerProperty">
    <value>1</value>
  </property>
</bean>

Collections

<list/><set/><map/><props/> 元素分别设置了 Java 集合类型 List、Set、Map 和 Properties的属性和参数。

而这些元素里包含的内容与<Property />一样,也包括以下内容:

bean | ref | idref | list | set | map | props | value | null

下面是定义一个Bean中如果存在上述的Java类型时的定义代码:

<bean id="moreComplexObject" class="com.study.spring.ioc.testbean.ComplexObject">
  <!--  java.util.Properties  -->
  <property name="adminEmails">
    <props>
      <prop key="administrator">administrator@email.com</prop>
      <prop key="support">support@email.com</prop>
      <prop key="development">development@email.com</prop>
    </props>
  </property>
  <!--  java.util.List  -->
  <property name="someList">
    <list>
      <value>a list element followed by a reference</value>
      <ref bean="anotherBean"/>
    </list>
  </property>
  <!--  java.util.Map  -->
  <property name="someMap">
    <map>
      <entry key="an entry" value="just some string"/>
      <entry key="a ref" value-ref="anotherBean"/>
    </map>
  </property>
  <!--  java.util.Set  -->
  <property name="someSet">
    <set>
      <value>just some string</value>
      <ref bean="anotherBean"/>
    </set>
  </property>
</bean>
public class ComplexObject {

    private Properties adminEmails;
    private List<Object> someList;
    private Map<String, Object> someMap;
    private Set<Object> someSet;

    // ... 省略 getter 和 setter 方法
}

Collection Merging

在 Spring 中提供了父子bean的定义方式,子bean可通过parent属性指定父Bean,如果父Bean中已经定义了一些属性,子Bean可以直接使用父Bean中的属性值,也可以将父Bean中的属性值进行覆盖,也可以添加一些新的属性值。在父Bean存在集合类的属性时,除了这几种情况,也可以将子bean和父Bean的集合属性值进行合并。合并的方式就是在<list/><set/><map/><props/> 等集合元素中添加merge=true这个属性。需要注意的是,因为List是一个有序集合,因此为了保持有序集合的概念,父列表的值将优先于子列表的值,其它几种集合不存在排序语义。

配置父 Bean

<bean id="parent" abstract="true" class="com.study.spring.ioc.testbean.ComplexObject">
  <property name="adminEmails">
    <props>
      <prop key="administrator">administrator@email.com</prop>
      <prop key="support">support@email.com</prop>
    </props>
  </property>
  <property name="someList">
    <list>
      <value type="java.lang.String">this is parent</value>
    </list>
  </property>
  <property name="someMap">
    <map merge="true">
      <entry key="p" value="parent"/>
      <entry key="p-ref" value-ref="anotherBean"/>
    </map>
  </property>
</bean>

配置子 Bean

<bean id="child" parent="parent">
  <property name="adminEmails">
    <!-- merge 属性在子 Bean 的集合属性中开启,子 Bean 的集合属性即会合并父 Bean 的该属性 -->
    <props merge="true">
      <prop key="sales">sales@email.com</prop>
      <!-- 该属性的key和 父 bean 中定义的一样,因此会覆盖父 Bean 中的值-->
      <prop key="support">support@email.cn</prop>
    </props>
  </property>
  <property name="someList">
    <list merge="true">
      <value type="java.lang.String">this is child</value>
    </list>
  </property>
  <property name="someMap">
    <map merge="true">
      <entry key="c" value="child"/>
      <entry key="c-ref" value-ref="yetAnotherBean"/>
    </map>
  </property>
</bean>

指定集合类型

Spring将强类型集合依赖注入到Bean时,会将强类型集合实例的元素添加到集合之前将其转换为相应的类型。

public class SomeClass {

	private Map<String, Float> accounts;

	public void setAccounts(Map<String, Float> accounts) {
		this.accounts = accounts;
	}
}
<beans>
  <bean id="something" class="x.y.SomeClass">
    <property name="accounts">
      <map>
        <entry key="one" value="9.99"/>
        <entry key="two" value="2.75"/>
        <entry key="six" value="3.99"/>
      </map>
    </property>
  </bean>
</beans>

null 和 空字符串

  • 空字符串 当声明Bean时,<property/>value属性设置为空字符串时,Bean在实例化后改属性的值就是空字符串

    <bean class="ExampleBean">
      <property name="email" value=""/>
    </bean>
    
  • null

    如果在声明Bean需要将属性设为null值时,可以在<property/>元素中使用<null/>元素声明

    <bean class="ExampleBean">
    	<property name="email">
    		<null/>
    	</property>
    </bean>
    

简写Bean配置

在Bean的声明文件中,可以使用namespace的配置来简写<property /><constructor-arg />元素,可已在Bean上通过属性进行配置。

使用p-namespace

在Bean的声明文件中,配置p-namespace,使<bean/>元素通过属性来描述Bean,省略了<property/>元素。具体的配置就是在<beans/>元素中添加xmlns:p="http://www.springframework.org/schema/p"属性。之后再<bean/>元素中就可以通过p:属性=""的形式进行配置了。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- 使用 property 元素配置 -->
	<bean name="john-classic" class="com.example.Person">
		<property name="name" value="John Doe"/>
		<property name="spouse" ref="jane"/>
	</bean>

  <!-- 使用 p-namespace Bean 属性配置 -->
	<bean name="john-modern"
		class="com.example.Person"
		p:name="John Doe"
		p:spouse-ref="jane"/>

	<bean name="jane" class="com.example.Person">
		<property name="name" value="Jane Doe"/>
	</bean>
</beans>

使用c-namespace

与使用p-namespace来省略<property />元素一致,可以使用c-namespace来省略<constructor-arg />元素。配置方式也和p-namespace类似,在<beans/>元素中添加xmlns:c="http://www.springframework.org/schema/c"属性。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:c="http://www.springframework.org/schema/c"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="beanTwo" class="x.y.ThingTwo"/>
	<bean id="beanThree" class="x.y.ThingThree"/>

	<!-- traditional declaration with optional argument names -->
	<bean id="beanOne" class="x.y.ThingOne">
		<constructor-arg name="thingTwo" ref="beanTwo"/>
		<constructor-arg name="thingThree" ref="beanThree"/>
		<constructor-arg name="email" value="something@somewhere.com"/>
	</bean>

	<!-- c-namespace declaration with argument names -->
	<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
		c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

depends-on

dependes-on<bean/>元素的一个属性,它显示的声明了该Bean所需的依赖Bean,Spring会在初始化这个Bean之前初始化depends-on中声明的Bean。通常情况下可以用<ref/>元素代替,但当Bean的依赖关系不是很直接时,就需要显示声明。例如,需要触发一个类中的静态初始化器,如数据库驱动程序注册。depends-on 属性可以显式地强制在使用此元素的 Bean 初始化之前初始化一个或多个 Bean。

dependes-on属性可以配置多个Bean,可以用逗号、空格、分号隔开各个Bean。

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
  <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

Bean 的懒加载

在默认情况下,ApplicationContext的实现会创建和配置所有的单例Bean,作为初始化的一部分。这样的好处就是,如果Bean的配置存在问题,会立即被发现。但如果有特殊情况,需要懒加载Bean时,可以在配置元数据文件中的<bean/>元素中添加lazy-init="true"l来开启懒加载。但如果,已被声明为懒加载的单例Bean被其他没有声明为懒加载的Bean引用时,则被声明为懒加载的Bean则会在初始化时就被配置,懒加载的配置就不启作用了。

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>