bean定义,map,list,set,pros,数组,以及集合的合并

1.id,name
bean的命名

bean命名约定

bean的命名采用标准的Java命名约定,即小写字母开头,首字母大写间隔 的命名方式。如accountManager、 accountService 、userDao及loginController,等等。

对bean采用统一的命名约定将会使配置更加简单易懂。而且在使用Spring AOP时 ,如果要发通知(advice)给与一组名称相关的bean时,这种简单的命名方式将会令你受益匪浅。

每个bean都有一个或多个id(或称之为标识符或名称,在术语 上可以理解成一回事)。这些id在当前IoC容器中必须唯一。如果 一个bean有多个id,那么其他的id在本质上将被认为是别名。

当使用基于XML的配置元数据时,将通过id或 name属性来指定bean标识符。id属性具有唯一性, 而且是一个真正的XML ID属性,因此其他xml元素在引用该id时,可以利用XML解析器的 验证功能。通常情况下最好为bean指定一个id。尽管XML规范规定了XML ID命名的有效 字符,但是bean标识符的定义不受该限制,因为除了使用指定的XML字符来作为id,还可 以为bean指定别名,要实现这一点可以在name属性中使用逗号、 冒号或者空格将多个id分隔。

值得注意的是,为一个bean提供一个name并不是必须的,如果没有指定,那么容 器将为其生成一个惟一的name。对于不指定name属性的原因我们会在后面介绍(比如 内部bean就不需要)。

2.alias
bean的别名
在对bean进行定义时,除了使用id属性来指定名称 之外,为了提供多个名称,需要通过name属性来加以指定 。而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如 为了让应用的每一个组件能更容易的对公共组件进行引用。

然而,在定义bean时就指定所有的别名并不是总是恰当的。有时我们期望 能在当前位置为那些在别处定义的bean引入别名。在XML配置文件中,可用 <alias/> 元素来完成bean别名的定义。如:

<alias name="fromName" alias="toName"/>
这里如果在容器中存在名为fromName的bean定义, 在增加别名定义之后,也可以用toName来引用。

考虑一个更为具体的例子,组件A在XML配置文件中定义了一个名为 componentA-dataSource的DataSource bean。但组件B却想在其XML文件中 以componentB-dataSource的名字来引用此bean。而且在主程序MyApp的XML配 置文件中,希望以myApp-dataSource的名字来引用此bean。最后容器加载三个 XML文件来生成最终的ApplicationContext,在此情形下,可通过在MyApp XML 文件中添加下列alias元素来实现:

<alias name="componentA-dataSource" alias="componentB-dataSource"/>
<alias name="componentA-dataSource" alias="myApp-dataSource" />
这样一来,每个组件及主程序就可通过唯一名字来引用同一个数据源而互不干扰。

3.class
实例化bean
普通类不说明
内部类名

如果需要你希望将一个静态的内部类配置为 一个bean的话,那么内部类的名字需要采用$符号。

比如说,在com.example包下有一个叫 Foo的类,而Foo类有一个静态 的内部类叫Bar,那么在bean定义的时候, class属性必须这样写:

com.example.Foo$Bar

注意这里我们使用了$字符将内部类和外部类进行分隔

从本质上来说,bean定义描述了如何创建一个或多个对象实例。当需要的时候, 容器会从bean定义列表中取得一个指定的bean定义,并根据bean定义里面的配置元数据 使用反射机制来创建(或取得)一个实际的对象。

当采用XML描述配置元数据时,将通过<bean/>元素的 class属性来指定实例化对象的类型。class 属性 (对应BeanDefinition实例的 Class属性)通常是必须的(不过也有两种例外的情形,可以使用实例化工厂方法来管理bean,或bean定义的继承。class属性主要有两种用途 :在大多数情况下,容器将直接通过反射调用指定类的构造器来创建bean(这有点类似于 在Java代码中使用new操作符);在极少数情况下,容器将调用 类的静态工厂方法来创建bean实例,class 属性将用来指定实际具有静态工厂方法的类(至于调用静态工厂 方法创建的对象类型是当前class还是其他的class则无关紧要)。

也可以指定构造方法
默认情况下class可能需要一个默认的空构造器。
一些特殊的类,不存在空构造,需要指定构造器的依赖,查看第四节

使用静态工厂方法实例化
采用静态工厂方法创建bean时,除了需要指定class 属性外,还需要通过factory-method属性来指定创建bean实例 的工厂方法。Spring将调用此方法(其可选参数接下来介绍)返回实例对象,就此而言, 跟通过普通构造器创建类实例没什么两样。
下面的bean定义展示了如何通过工厂方法来创建bean实例。注意,此定义并 未指定返回对象的类型,仅指定该类包含的工厂方法。在此例中, createInstance()必须是一个static方法。详细的使用可以看第四节

<bean id="exampleBean"
      class="examples.ExampleBean2"
      factory-method="createInstance"/>
注入依赖项查看第四节

使用实例工厂方法实例化
与 使用静态工厂方法实例化类似,用来进行实例化的非静态实例工厂方法位 于另外一个bean中,容器将调用该bean的工厂方法来创建一个新的bean实例。为使 用此机制,class属性必须为空,而factory-bean 属性必须指定为当前(或其祖先)容器中包含工厂方法的bean的名称,而该 工厂bean的工厂方法本身必须通过factory-method属性来设定。详细的使用可以看第四节

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="com.foo.DefaultServiceLocator">
  <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="exampleBean"
      factory-bean="serviceLocator"
      factory-method="createInstance"/>
注入依赖项查看第四节

4.注入依赖项
依赖注入(DI)背后的基本原理是对象之间的依赖关系(即一起工作的其它对象)只会通过以下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。因此,容器的工作就是创建bean时注入那些依赖关系。相对于由bean自己来控制其实例化、直接在构造器中指定依赖关系或者类似服务定位器(Service Locator)模式这3种自主控制依赖关系注入的方法来说,控制从根本上发生了倒转,这也正是控制反转(Inversion of Control, IoC) 名字的由来。

应用DI原则后,代码将更加清晰。而且当bean自己不再担心对象之间的依赖关系(甚至不知道依赖的定义指定地方和依赖的实际类)之后,实现更高层次的松耦合将易如反掌。DI主要有两种注入方式,即Setter注入和构造器注入

构造器的参数注入
通常情况下(每个参数不存在IS-A关系),顺序可以不按定义的参数顺序传入。如:
package com.liyixing.spring.service.imp;

import com.liyixing.spring.model.Account;
import com.liyixing.spring.service.IAccount;

public class AccountService implements IAccount {

public AccountService(Account account, String name) {
System.out.println("删除用户" + account);
System.out.println("删除用户:name " + name);
}

public void deleteAccount(Account account) {
System.out.println("删除用户");
}

}

构造器AccountService的参数Account 和name不存在继承关系,那么

beans.xml
<?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-2.5.xsd">
<bean id="accountService" class="com.liyixing.spring.service.imp.AccountService">
<constructor-arg>
<value>liyixing</value>
</constructor-arg>
<constructor-arg>
<!--这里会生成一个新的bean,并把这个bean作为参数传入-->
<bean class="com.liyixing.spring.model.Account"></bean>
</constructor-arg>
</bean>
</beans>
上面的定义中可以看到参数虽然和构造器的定义顺序不一样,但是还是成功了。

再看一个例子
package com.liyixing.spring.service.imp;

import com.liyixing.spring.model.Account;
import com.liyixing.spring.service.IAccount;

public class AccountService implements IAccount {

public AccountService(String name, int age) {
System.out.println("name" + name);
System.out.println("age " + age);
}

public void deleteAccount(Account account) {
System.out.println("删除用户");
}

}
<?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-2.5.xsd">
<bean id="accountService" class="com.liyixing.spring.service.imp.AccountService">
<constructor-arg>
<value>1</value>
</constructor-arg>
<constructor-arg>
<value>liyixing</value>
</constructor-arg>
</bean>
</beans>
这里的配置中,使用了value对参数传值。有需要特殊处理,它才能知道实际的类型。
<?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-2.5.xsd">
<bean id="accountService" class="com.liyixing.spring.service.imp.AccountService">
<constructor-arg>
<value type="int">1</value>
</constructor-arg>
<constructor-arg>
<value>liyixing</value>
</constructor-arg>
</bean>
</beans>
这里加上了type属性,为int,就可以处理了。
们还可以通过index属性来显式指定构造参数的索引
<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg index="0" value="7500000"/>
  <constructor-arg index="1" value="42"/>
</bean>

Setter注入
采用构造器注入的方式配置bean时,很有可能会产生循环依赖的情况。

比如说,一个类A,需要通过构造器注入类B,而类B又需要通过构造器注入类A。如果为类A和B配置的bean被互相注入的话,那么Spring IoC容器将检测出循环引用,并抛出 BeanCurrentlyInCreationException异常。

对于此问题,一个可能的解决方法就是修改源代码,将某些构造器注入改为setter注入。另一个解决方法就是完全放弃构造器注入,只使用setter注入。换句话说,除了极少数例外,大部分的循环依赖都是可以避免的,不过采用setter注入产生循环依赖的可能性也是存在的。

与通常我们见到的非循环依赖的情况有所不同,在两个bean之间的循环依赖将导致一个bean在被完全初始化的时候被注入到另一个bean中(如同我们常说的先有蛋还是先有鸡的情况)。

通常情况下,你可以信赖Spring,它会在容器加载时发现配置错误(比如对无效bean的引用以及循环依赖)。Spring会在bean创建时才去设置属性和依赖关系(只在需要时创建所依赖的其他对象)。这意味着即使Spring容器被正确加载,当获取一个bean实例时,如果在创建bean或者设置依赖时出现问题,仍然会抛出一个异常。因缺少或设置了一个无效属性而导致抛出一个异常的情况的确是存在的。因为一些配置问题而导致潜在的可见性被延迟,所以在默认情况下,ApplicationContext实现中的bean采用提前实例化的singleton模式。在实际需要之前创建这些bean将带来时间与内存的开销。而这样做的好处就是ApplicationContext被加载的时候可以尽早的发现一些配置的问题。不过用户也可以根据需要采用延迟实例化来替代默认的singleton模式。

如果撇开循环依赖不谈,当协作bean被注入到依赖bean时,协作bean必须在依赖bean之前完全配置好。例如bean A对bean B存在依赖关系,那么Spring IoC容器在调用bean A的setter方法之前,bean B必须被完全配置,这里所谓完全配置的意思就是bean将被实例化(如果不是采用提前实例化的singleton模式),相关的依赖也将被设置好,而且所有相关的lifecycle方法(如IntializingBean的init方法以及callback方法)也将被调用。

例子

package com.liyixing.spring.service.imp;

import com.liyixing.spring.model.Account;
import com.liyixing.spring.service.IAccount;

public class AccountService implements IAccount {

public AccountService(String name, int age) {
System.out.println("name" + name);
System.out.println("age " + age);
}

public void deleteAccount(Account account) {
System.out.println("删除用户");
}

}


package com.liyixing.spring.service.imp;

import com.liyixing.spring.model.Account;
import com.liyixing.spring.model.Download;
import com.liyixing.spring.service.IAccount;
import com.liyixing.spring.service.IDownload;

public class DownloadService implements IDownload {
private IAccount accountService;

public IAccount getAccountService() {
return accountService;
}

public void setAccountService(IAccount accountService) {
this.accountService = accountService;
}

public Download getMyDown(Account account) {
System.out.println("accountService" + accountService);
System.out.println("获取我的下载");
return null;
}

}

<?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-2.5.xsd">
<bean id="accountService" class="com.liyixing.spring.service.imp.AccountService">
<constructor-arg>
<value type="int">1</value>
</constructor-arg>
<constructor-arg>
<value>liyixing</value>
</constructor-arg>
</bean>

<bean id="downloadService" class="com.liyixing.spring.service.imp.DownloadService">
<!--setter通过property设置-->
<property name="accountService">
<ref local="accountService"/>
</property>
</bean>
</beans>
<?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-2.5.xsd">
<bean id="accountService" class="com.liyixing.spring.service.imp.AccountService">
<constructor-arg>
<value type="int">1</value>
</constructor-arg>
<constructor-arg>
<value>liyixing</value>
</constructor-arg>
</bean>

<bean id="downloadService" class="com.liyixing.spring.service.imp.DownloadService">
<property name="accountService">
<ref local="accountService"/>
</property>
</bean>
</beans>
输出结果



这里同时也看出来spring默认是单例模式的。

静态工厂方法的例子
修改DownloadServie类

package com.liyixing.spring.service.imp;

import com.liyixing.spring.model.Account;
import com.liyixing.spring.model.Download;
import com.liyixing.spring.service.IAccount;
import com.liyixing.spring.service.IDownload;

public class DownloadService implements IDownload {
private IAccount accountService;

public IAccount getAccountService() {
return accountService;
}

public void setAccountService(IAccount accountService) {
this.accountService = accountService;
}

public Download getMyDown(Account account) {
System.out.println("accountService" + accountService);
System.out.println("获取我的下载");
return null;
}

public static DownloadService getInstance(IAccount accountService) {
DownloadService down = new DownloadService();
down.setAccountService(accountService);

return down;
}

}
public static DownloadService getInstance这个方法的返回类型也可以为别的。
<?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-2.5.xsd">
<bean id="accountService" class="com.liyixing.spring.service.imp.AccountService">
<constructor-arg>
<value type="int">1</value>
</constructor-arg>
<constructor-arg>
<value>liyixing</value>
</constructor-arg>
</bean>

<bean id="downloadService" class="com.liyixing.spring.service.imp.DownloadService"
factory-method="getInstance">
<constructor-arg>
<ref local="accountService"/>
</constructor-arg>
</bean>
</beans>
配置中可以看到静态工厂的参数也是用<constructor-arg>来设置。

bean工厂方式与其类似,使用factory-bean属性替代class属性。

依赖配置详解
直接变量(基本类型、Strings类型等。)
<value/>元素通过人可以理解的字符串来指定属性或构造器参数的值。正如前面所提到的,JavaBean PropertyEditor将用于把字符串从java.lang.String类型转化为实际的属性或参数类型。

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 
  <!-- results in a setDriverClassName(String) call -->
  <property name="driverClassName">
    <value>com.mysql.jdbc.Driver</value>
  </property>
  <property name="url">
    <value>jdbc:mysql://localhost:3306/mydb</value>
  </property>
  <property name="username">
    <value>root</value>
  </property>
  <property name="password">
    <value>masterkaoli</value>
  </property>
</bean>

property/> 和<constructor-arg/> 元素中也可以使用'value' 属性,这样会使我们的配置更简洁,比如下面的配置:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 
  <!-- results in a setDriverClassName(String) call -->
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
  <property name="username" value="root"/>
  <property name="password" value="masterkaoli"/>
</bean>

Spring团队更倾向采用属性方式(使用<value/>元素,在里面传递xxx=xxx的方式)来定义value值。当然我们也可以按照下面这种方式配置一个java.util.Properties实例:

<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
           
   <!-- typed as a java.util.Properties -->
   <property name="properties">
      <value>
         jdbc.driver.className=com.mysql.jdbc.Driver
         jdbc.url=jdbc:mysql://localhost:3306/mydb
      </value>
   </property>
</bean>
pring容器将使用JavaBean PropertyEditor把<value/>元素中的文本转换为一个java.util.Properties实例。由于这种做法的简单,因此Spring团队在很多地方也会采用内嵌的<value/>元素来代替value属性。

idref元素

idref元素用来将容器内其它bean的id传给<constructor-arg/> 或 <property/>元素,同时提供错误验证功能。

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean" />
    </property>
</bean>
上述bean定义片段完全地等同于(在运行时)以下的片段:
<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" ref="theTargetBean" />
</bean>
第一种形式比第二种更可取的主要原因是,使用idref标记允许容器在部署时 验证所被引用的bean是否存在。而第二种方式中,传给client bean的targetName属性值并没有被验证。任何的输入错误仅在client bean实际实例化时才会被发现(可能伴随着致命的错误)。如果client bean 是prototype类型的bean,则此输入错误(及由此导致的异常)可能在容器部署很久以后才会被发现。

此外,如果被引用的bean在同一XML文件内,且bean名字就是bean id,那么可以使用local属性,此属性允许XML解析器在解析XML文件时对引用的bean进行验证。

<property name="targetName">
   <!-- a bean with an id of 'theTargetBean' must exist; otherwise an XML exception will be thrown -->
   <idref local="theTargetBean"/>
</property>
上面的例子中,与在ProxyFactoryBean bean定义中使用<idref/>元素指定AOP interceptor的相同之处在于:如果使用<idref/>元素指定拦截器名字,可以避免因一时疏忽导致的拦截器ID拼写错误。
引用其它的bean(协作者)

在<constructor-arg/>或<property/>元素内部还可以使用ref元素。该元素用来将bean中指定属性的值设置为对容器中的另外一个bean的引用。如前所述,该引用bean将被作为依赖注入,而且在注入之前会被初始化(如果是singleton bean则已被容器初始化)。尽管都是对另外一个对象的引用,但是通过id/name指向另外一个对象却有三种不同的形式,不同的形式将决定如何处理作用域及验证。

第一种形式也是最常见的形式是通过使用<ref/>标记指定bean属性的目标bean,通过该标签可以引用同一容器或父容器内的任何bean(无论是否在同一XML文件中)。XML 'bean'元素的值既可以是指定bean的id值也可以是其name值。

<ref bean="someBean"/>
第二种形式是使用ref的local属性指定目标bean,它可以利用XML解析器来验证所引用的bean是否存在同一文件中。local属性值必须是目标bean的id属性值。如果在同一配置文件中没有找到引用的bean,XML解析器将抛出一个例外。如果目标bean是在同一文件内,使用local方式就是最好的选择(为了尽早地发现错误)。

<ref local="someBean"/>

第三种方式是通过使用ref的parent属性来引用当前容器的父容器中的bean。parent属性值既可以是目标bean的id值,也可以是name属性值。而且目标bean必须在当前容器的父容器中。使用parent属性的主要用途是为了用某个与父容器中的bean同名的代理来包装父容器中的一个bean(例如,子上下文中的一个bean定义覆盖了他的父bean)。

<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService"  <-- notice that the name of this bean is the same as the name of the 'parent' bean
      class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="target">
          <ref parent="accountService"/>  <-- notice how we refer to the parent bean
      </property>
    <!-- insert other configuration and dependencies as required as here -->
</bean>

内部bean

所谓的内部bean(inner bean)是指在一个bean的<property/>或 <constructor-arg/>元素中使用<bean/>元素定义的bean。内部bean定义不需要有id或name属性,即使指定id 或 name属性值也将会被容器忽略。

<bean id="outer" class="...">
  <!-- instead of using a reference to a target bean, simply define the target bean inline -->
  <property name="target">
    <bean class="com.example.Person"> <!-- this is the inner bean -->
      <property name="name" value="Fiona Apple"/>
      <property name="age" value="25"/>
    </bean>
  </property>
</bean>

集合

通过<list/>、<set/>、<map/>及<props/>元素可以定义和设置与Java Collection类型对应List、Set、Map及Properties的值。

<bean id="moreComplexObject" class="example.ComplexObject">
  <!-- results in a setAdminEmails(java.util.Properties) call -->
  <property name="adminEmails">
    <props>
        <prop key="administrator">[email protected]</prop>
        <prop key="support">[email protected]</prop>
        <prop key="development">[email protected]</prop>
    </props>
  </property>
  <!-- results in a setSomeList(java.util.List) call -->
  <property name="someList">
    <list>
        <value>a list element followed by a reference</value>
        <ref bean="myDataSource" />
    </list>
  </property>
  <!-- results in a setSomeMap(java.util.Map) call -->
  <property name="someMap">
    <map>
        <entry>
            <key>
                <value>an entry</value>
            </key>
            <value>just some string</value>
        </entry>
        <entry>
            <key>
                <value>a ref</value>
            </key>
            <ref bean="myDataSource" />
        </entry>
    </map>
  </property>
  <!-- results in a setSomeSet(java.util.Set) call -->
  <property name="someSet">
    <set>
        <value>just some string</value>
        <ref bean="myDataSource" />
    </set>
  </property>
</bean>
注意:map的key或value值,或set的value值还可以是以下元素:

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

集合的合并
从2.0开始,Spring IoC容器将支持集合的合并。这样我们可以定义parent-style和child-style的<list/>、<map/>、<set/>或<props/>元素,子集合的值从其父集合继承和覆盖而来;也就是说,父子集合元素合并后的值就是子集合中的最终结果,而且子集合中的元素值将覆盖父集全中对应的值。

<bean id="account" class="com.liyixing.spring.model.Account"
parent="parent">
<property name="p">
<props merge="true">
<prop key="support">my</prop>
</props>
</property>
</bean>
<bean id="parent" abstract="true" class="com.liyixing.spring.model.Account">
<property name="p">
<props>
<prop key="a">[email protected]</prop>
<prop key="support">[email protected]</prop>
</props>
</property>
</bean>
这里的区别在 <props merge="true"> merge=true这个表示合并。相同的key将会被覆盖

另外我们也可以把它拆分成多个文件
在bean.xml中定义
<bean id="account" class="com.liyixing.spring.model.Account"
parent="parent">
<property name="p">
<props merge="true">
<prop key="support">my</prop>
</props>
</property>
</bean>
在bean1.xml定义

<bean id="parent" abstract="true" class="com.liyixing.spring.model.Account">
<property name="p">
<props>
<prop key="a">[email protected]</prop>
<prop key="support">[email protected]</prop>
</props>
</property>
</bean>
依然是一样的。

list,我这里把parent和account定义在两个文件

package com.liyixing.spring.model;

import java.util.List;
import java.util.Properties;

public class Account {
private Properties p;
private List<String> l;

public List<String> getL() {
return l;
}

public void setL(List<String> l) {
this.l = l;
}

public Properties getP() {
return p;
}

public void setP(Properties p) {
this.p = p;
}
}
bean1.xml
<bean id="parent" abstract="true" class="com.liyixing.spring.model.Account">
<property name="p">
<props>
<prop key="a">[email protected]</prop>
<prop key="support">[email protected]</prop>
</props>
</property>

<property name="l">
<list>
<value>1iyixing</value>
<value>1iyimao</value>
</list>
</property>
</bean>
</beans>
bean.xml

<bean id="account" class="com.liyixing.spring.model.Account"
parent="parent">
<property name="p">
<props merge="true">
<prop key="support">my</prop>
</props>
</property>

<property name="l">
<list merge="true">
<value>test</value>
</list>
</property>
</bean>

测试,可以看到


两个list合并到了一起。
这个功能我以前一直想找找怎么做,可以让DataSource的映射列表定义成多个文件。一直没找到,今天看到了spring的开发手册的这个功能,应该能实现了。

强类型集合(仅适用于Java5+)

你若有幸在使用Java5 或Java 6,那么你可以使用强类型集合(支持泛型)。比如,声明一个只能包含String类型元素的Collection。假若使用Spring来给bean注入强类型的Collection,那就可以利用Spring的类型转换能,当向强类型Collection中添加元素前,这些元素将被转换。

public class Foo {
               
    private Map<String, Float> accounts;
   
    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="foo" class="x.y.Foo">
        <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>
在foobean的accounts属性被注入之前,通过反射,利用强类型Map<String, Float>的泛型信息,Spring的底层类型转换机制将会把各种value元素值转换为Float类型,因此字符串9.99、2.75及3.99就会被转换为实际的Float类型。

Nulls

<null/>用于处理null值。Spring会把属性的空参数当作空字符串处理。以下的xml片断将email属性设为空字符串。

<bean class="ExampleBean">
  <property name="email"><value/></property>
</bean>
这等同于Java代码: exampleBean.setEmail("")。 而null值则可以使用<null>元素可用来表示。例如:

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

5.XML配置文件的简写及其他

配置元数据冗长不是什么好事情,因此我们将通过下面的方式来对配置进行“减肥”,第一种做法就是通过使用<property/>来定义值和对其他bean的引用,另一个做法就是采用不同的属性定义格式。

XML-based configuration metadata shortcuts

<property/>、<constructor-arg/>及<entry/>元素都支持value属性(attribute),它可以用来替代内嵌的<value/>元素。因而,以下的代码:

<property name="myProperty">
  <value>hello</value>
</property>
<constructor-arg>
  <value>hello</value>
</constructor-arg>
<entry key="myKey">
  <value>hello</value>
</entry>
等同于:

<property name="myProperty" value="hello"/>
<constructor-arg value="hello"/>
<entry key="myKey" value="hello"/>
The <property/> and <constructor-arg/> elements support a similar shortcut 'ref' attribute which may be used instead of a full nested <ref/> element. Therefore, the following:

<property/>和<constructor-arg/>支持类似ref的简写属性,它可用来替代整个内嵌的<ref/>元素。因而,以下的代码:

<property name="myProperty">
  <ref bean="myBean">
</property>
<constructor-arg>
  <ref bean="myBean">
</constructor-arg>
等同于:

<property name="myProperty" ref="myBean"/>
<constructor-arg ref="myBean"/>
注意,尽管存在等同于<ref bean="xxx"> 元素的简写形式,但并没有<ref local="xxx">的简写形式,为了对当前xml中bean的引用,你只能使用完整的形式。

最后,map中entry元素的简写形式为key/key-ref 和 value /value-ref属性,因而,以下的代码:

<entry>
  <key>
    <ref bean="myKeyBean" />
  </key>
  <ref bean="myValueBean" />
</entry>
等同于:

<entry key-ref="myKeyBean" value-ref="myValueBean"/>
再次强调,只有<ref bean="xxx">元素的简写形式,没有<ref local="xxx">的简写形式。

使用p名称空间配置属性

给XML配置文件"减肥"的另一个选择就是使用p名称空间,从 2.0开始,Spring支持使用名称空间的可扩展配置格式。这些名称空间都是基于一种XML Schema定义。事实上,我们所看到的所有bean的配置格式都是基于一个 XML Schema文档。

特定的名称空间并不需要定义在一个XSD文件中,它只在Spring内核中存在。我们所说的p名称空间就是这样,它不需要一个schema定义,与我们前面采用<property/>元素定义bean的属性不同的是,当我们采用了p名称空间,我们就可以在bean元素中使用属性(attribute)来描述bean的property值。

下面的两段XML配置文件中都是用来定义同一个bean:一个采用的是标准的XML格式,一个是采用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
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
   
    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="[email protected]/>
    </bean>
   
    <bean name="p-namespace" class="com.example.ExampleBean"
          p:email="[email protected]"/>
</beans>
从上面的bean定义中,我们采用p名称空间的方式包含了一个叫email的属性,而Spring会知道我们的bean包含了一个属性(property)定义。我们前面说了,p名称空间是不需要schema定义的,因此属性(attribute)的名字就是你bean的property的名字。

This next example includes two more bean definitions that both have a reference to another bean:

下面的例子包含了两个bean定义,它们都引用了另一个bean

<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
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
   
    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </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>
As you can see, this example doesn't only include a property value using the p-namespace, but also uses a special format to declare property references. Whereas the first bean definition uses <property name="spouse" ref="jane"/> to create a reference from bean john to bean jane, the second bean definition uses p:spouse-ref="jane" as an attribute to do the exact same thing. In this case 'spouse' is the property name whereas the '-ref' part indicates that this is not a straight value but rather a reference to another bean.

上面的例子不仅使用p名称空间包含了一个属性(property)值,而且使用了一个特殊的格式声明了一个属性引用。在第一个bean定义中使用了<property name="spouse" ref="jane"/>来建立beanjohn到beanjane的引用,而第二个bean定义则采用p:spouse-ref="jane"属性(attribute)的方式达到了同样的目的。在这个例子中,"spouse"是属性(property)名,而"-ref“则用来说明该属性不是一个具体的值而是对另外一个bean的引用。

Note
需要注意的是,p名称空间没有标准的XML格式定义灵活,比如说,bean的属性名是以Ref结尾的,那么采用p名称空间定义就会导致冲突,而采用标准的XML格式定义则不会出现这种问题。这里我们提醒大家在项目中还是仔细权衡来决定到底采用那种方式,同时也可以在团队成员都理解不同的定义方式的基础上,在项目中根据需要同时选择三种定义方式。


组合属性名称
当设置bean的组合属性时,除了最后一个属性外,只要其他属性值不为null,组合或嵌套属性名是完全合法的。例如,下面bean的定义:

<bean id="foo" class="foo.Bar">
  <property name="fred.bob.sammy" value="123" />
</bean>
foo bean有个fred属性,此属性有个bob属性,而bob属性又有个sammy属性,最后把sammy属性设置为123。为了让此定义能工作, foo的fred属性及fred的bob属性在bean被构造后都必须非空,否则将抛出NullPointerException异常

使用depends-on

多数情况下,一个bean对另一个bean的依赖最简单的做法就是将一个bean设置为另外一个bean的属性。在xml配置文件中最常见的就是使用 <ref/>元素。在少数情况下,有时候bean之间的依赖关系并不是那么的直接(例如,当类中的静态块的初始化被时,如数据库驱动的注册)。depends-on属性可以用于当前bean初始化之前显式地强制一个或多个bean被初始化。下面的例子中使用了depends-on属性来指定一个bean的依赖。

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>

<bean id="manager" class="ManagerBean" />
若需要表达对多个bean的依赖,可以在'depends-on'中将指定的多个bean名字用分隔符进行分隔,分隔符可以是逗号、空格及分号等。下面的例子中使用了'depends-on'来表达对多个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" />
Note
“depends-on”属性不仅用来指定初始化时的依赖,同时也用来指定相应的销毁时的依赖(该依赖只针对singletonbean)。depends-on属性中指定的依赖bean会在相关bean销毁之前被销毁,从而可以让用户控制销毁顺序。


延迟初始化
ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean。通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。

有时候这种默认处理可能并不是你想要的。如果你不想让一个singleton bean在ApplicationContext初始化时被提前实例化,那么可以将bean设置为延迟实例化。一个延迟初始化bean将告诉IoC 容器是在启动时还是在第一次被用到时实例化。

在XML配置文件中,延迟初始化将通过<bean/>元素中的lazy-init属性来进行控制。例如:

<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>

<bean name="not.lazy" class="com.foo.AnotherBean"/>
当ApplicationContext实现加载上述配置时,设置为lazy的bean将不会在ApplicationContext启动时提前被实例化

需要说明的是,如果一个bean被设置为延迟初始化,而另一个非延迟初始化的singleton bean依赖于它,那么当ApplicationContext提前实例化singleton bean时,它必须也确保所有上述singleton 依赖bean也被预先初始化,当然也包括设置为延迟实例化的bean。因此,如果Ioc容器在启动的时候创建了那些设置为延迟实例化的bean的实例,你也不要觉得奇怪,因为那些延迟初始化的bean可能在配置的某个地方被注入到了一个非延迟初始化singleton bean里面。

在容器层次上通过在<beans/>元素上使用'default-lazy-init'属性来控制延迟初始化也是可能的。如下面的配置:

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

猜你喜欢

转载自liyixing1.iteye.com/blog/1036520