Chinaunix首页 | 论坛 | 博客
  • 博客访问: 534970
  • 博文数量: 260
  • 博客积分: 10435
  • 博客等级: 上将
  • 技术积分: 1939
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-24 14:50
文章分类

全部博文(260)

文章存档

2011年(22)

2010年(209)

2009年(29)

我的朋友

分类: Java

2010-04-19 15:38:35

4.3 BeanFactoryXML之旅

XML格式的容器信息管理方式是Spring提供的最为强大、支持最为全面的方式。从Spring的参考文档到各Spring相关书籍,都是按照XML的配置进行说明的,这部分内容可以让你充分领略到SpringIoC容器的魅力,以致于我们也不得不带你初次或者再次踏上SpringXML之旅。

4.3.1 

所有使用XML文件进行配置信息加载的Spring IoC容器,包括BeanFactoryApplicationContext的所有XML相应实现,都使用统一的XML格式。在Spring 2.0版本之前,这种格式由Spring提供的DTD规定,也就是说,所有的Spring容器加载的XML配置文件的头部,都需要以下形式的DOCTYPE声明:

 

"">

  ...

                                  

Spring 2.0版本之后,Spring在继续保持向前兼容的前提下,既可以继续使用DTD方式的DOCTYPE进行配置文件格式的限定,又引入了基于XML Schema的文档声明。所以,Spring 2.0之后,同样可以使用代码清单4-11所展示的基于XSD的文档声明。

代码清单4-11 基于XSDSpring配置文件文档声明

xmlns:xsi=""

xmlns:util=""

xmlns:jee=""

xmlns:lang=""

xmlns:aop=""

xmlns:tx=""

xsi:schemaLocation="

/spring-beans-2.0.xsd

/spring-util-2.0.xsd

/spring-jee-2.0.xsd

/spring-lang-2.0.xsd

/spring-aop-2.0.xsd

/spring-tx-2.0.xsd">

 

                                  

不过,不管使用哪一种形式的文档声明,实际上限定的元素基本上是相同的。让我们从最顶层的元素开始,看一下这两种文档声明都限定了哪些元素吧!

所有注册到容器的业务对象,在Spring中称之为Bean。所以,每一个对象在XML中的映射也自然而然地对应一个叫做的元素。既然容器最终可以管理所有的业务对象,那么在XML中把这些叫做的元素组织起来的,就叫做。多个组成一个很容易理解,不是吗?

1. 之唯我独尊

XML配置文件中最顶层的元素,它下面可以包含0或者1个和多个以及或者,如图4-4所示。

4-4 与下一层元素的关系

作为所有的“统帅”,它拥有相应的属性(attribute)对所辖的进行统一的默认行为设置,包括如下几个。

q default-lazy-init其值可以指定为true或者false,默认值为false。用来标志是否对所有的进行延迟初始化。

q default-autowire可以取值为nobyNamebyTypeconstructor以及autodetect。默认值为no,如果使用自动绑定的话,用来标志全体bean使用哪一种默认绑定方式。

q default-dependency-check可以取值noneobjectssimple以及all,默认值为none,即不做依赖检查。

q default-init-method如果所管辖的按照某种规则,都有同样名称的初始化方法的话,可以在这里统一指定这个初始化方法名,而不用在每一个上都重复单独指定。

q default-destroy-methoddefault-init-method相对应,如果所管辖的bean有按照某种规则使用了相同名称的对象销毁方法,可以通过这个属性统一指定。

注意 当然,如果你不清楚上面这些默认的属性具体有什么用,那也不必着急。在看完对的讲解之后,再回头来看,就会明了多了。给出这些信息,是想让你知道,如果在某个场景下需要对大部分都重复设置某些行为的话,可以回头看一下,利用是否可以减少这种不必要的工作。

2.

之所以把这几个元素放到一起讲解,是因为通常情况下它们不是必需的。不过,了解一下也没什么不好,不是吗?

l

可以通过在配置的文件中指定一些描述性的信息。通常情况下,该元素是省略的。当然,如果愿意,随时可以为我们效劳。

l

通常情况下,可以根据模块功能或者层次关系,将配置信息分门别类地放到多个配置文件中。在想加载主要配置文件,并将主要配置文件所依赖的配置文件同时加载时,可以在这个主要的配置文件中通过元素对其所依赖的配置文件进行引用。比如,如果A.xml中的定义可能依赖B.xml中的某些定义,那么就可以在A.xml中使用B.xml引入到A.xml,以类似于的形式。

但是,这个功能在我看来价值不大,因为容器实际上可以同时加载多个配置,没有必要非通过一个配置文件来加载所有配置。不过,或许在有些场景中使用这种方式比较方便也说不定。

l

可以通过为某些起一些“外号”(别名),通常情况下是为了减少输入。比如,假设有个,它的名称为dataSourceForMasterDatabase,你可以为其添加一个,像这样以后通过dataSourceForMasterDatabase或者masterDataSource来引用这个都可以,只要你觉得方便就行。

4.3.2 孤孤单单一个人

哦,错了,是孤孤单单一个Bean。每个业务对象作为个体,在SpringXML配置文件中是与元素一一对应的。窥一斑而知全豹,只要我们了解单个的业务对象是如何配置的,剩下的就可以“依葫芦画瓢”了。所以,让我们先从最简单的单一对象配置开始吧!如下代码演示了最基础的对象配置形式:

 

 

l id属性

通常,每个注册到容器的对象都需要一个唯一标志来将其与“同处一室”的“兄弟们”区分开来,就好像我们每一个人都有一个身份证号一样(重号的话就比较麻烦)。通过id属性来指定当前注册对象的beanName是什么。这里,通过id指定beanNamedjNewsListener。实际上,并非任何情况下都需要指定每个<bean>的id,有些情况下,id可以省略,比如后面会提到的内部以及不需要根据beanName明确依赖关系的场合等。

除了可以使用id来指定在容器中的标志,还可以使用name属性来指定的别名(alias)。比如,以上定义,我们还可以像如下代码这样,为其添加别名:

 

    name="/news/djNewsListener,dowJonesNewsListener"

    class="..impl.DowJonesNewsListener">

 

id属性相比,name属性的灵活之处在于,name可以使用id不能使用的一些字符,比如/。而且还可以通过逗号、空格或者冒号分割指定多个namename的作用跟使用id指定多个别名基本相同:

 

 

l class属性

每个注册到容器的对象都需要通过元素的class属性指定其类型,否则,容器可不知道这个对象到底是何方神圣。在大部分情况下,该属性是必须的。仅在少数情况下不需要指定,如后面将提到的在使用抽象配置模板的情况下。

4.3.3 Help Me, Help You

在大部分情况下,你不太可能选择单独“作战”,业务对象也是;各个业务对象之间会相互协作来更好地完成同一使命。这时,各个业务对象之间的相互依赖就是无法避免的。对象之间需要相互协作,在横向上它们存在一定的依赖性。而现在我们就是要看一下,在SpringIoC容器的XML配置中,应该如何表达这种依赖性。

既然业务对象现在都符合IoC的规则,那么要了解的表达方式其实也很简单,无非就是看一下构造方法注入和setter方法注入通过XML是如何表达的而已。那么,让我们开始吧!

1. 构造方法注入的XML之道

按照Spring的IoC容器配置格式,要通过构造方法注入方式,为当前业务对象注入其所依赖的对象,需要使用正常情况下,如以下代码所示:

 

 

   

 

 

   

 

 

对于元素,稍后会进行详细说明。这里你只需要知道,通过这个元素来指明容器将为djNewsProvider这个注入通过所引用的Bean实例。这种方式可能看起来或者编写起来不是很简洁,最新版本的Spring也支持配置简写形式,如以下代码所示:

 

 

 

 

简洁多了不是嘛?其实,无非就是表达方式不同而已,实际达到的效果是一样的。

有些时候,容器在加载XML配置的时候,因为某些原因,无法明确配置项与对象的构造方法参数列表的一一对应关系,就需要请type或者index属性出马。比如,对象存在多个构造方法,当参数列表数目相同而类型不同的时候,容器无法区分应该使用哪个构造方法来实例化对象,或者构造方法可能同时传入最少两个类型相同的对象。

l type属性

假设有一个对象定义如代码清单4-12所示。

代码清单4-12 随意声明的一个业务对象定义

public class MockBusinessObject {

  private String dependency1;

  private int    dependency2;

 

  public MockBusinessObject(String dependency)

  {

    this.dependency1 = dependency;

  }

 

  public MockBusinessObject(int dependency)

  {

    this.dependency2 = dependency;

  }

  ...

 

  @Override

  public String toString() {

    return new ToStringBuilder(this)

    .append("dependency1", dependency1)

    .append("dependency2", dependency2).toString();

  }

}

 

该类声明了两个构造方法,分别都只是传入一个参数,且参数类型不同。这时,我们可以进行配置,如以下代码所示:

 

 

    111111

 

 

如果从BeanFactory取得该对象并调用toString()查看的话,我们会发现Spring调用的是第一个构造方法,因为输出是如下内容:

 

..MockBusinessObject@f73c1[dependency1=111111,dependency2=0]

 

但是,如果我们想调用的却是第二个传入int类型参数的构造方法,又该如何呢?可以使用type属性,通过指定构造方法的参数类型来解决这一问题,配置内容如下代码所示:

 

 

    111111

 

 

现在,我们得到了自己想要的对象实例,如下的控制台输出信息印证了这一点:

 

..MockBusinessObject@f73c1[dependency1=,dependency2=111111]

l index属性

当某个业务对象的构造方法同时传入了多个类型相同的参数时,Spring又该如何将这些配置中的信息与实际对象的参数一一对应呢?好在,如果配置项信息和对象参数可以按照顺序初步对应的话,Spring还是可以正常工作的,如代码清单4-13所示。

代码清单4-13 随意声明的一个业务对象定义

public class MockBusinessObject {

  private String dependency1;

  private String dependency2;

 

  public MockBusinessObject(String dependency1,String    dependency2)

  {

    this.dependency1 = dependency1;

    this.dependency2 = dependency2;

  }

  ...

 

  @Override

  public String toString() {

    return new ToStringBuilder(this)

    .append("dependency1", dependency1)

    .append("dependency2", dependency2).toString();

  }

}

 

并且,配置内容如以下代码所示:

 

 

 

 

那么,我们可以得到如下对象:

 

..MockBusinessObject@1ef8cf3[dependency1=11111,dependency2=22222]

 

但是,如果要让“11111”作为对象的第二个参数,而将“22222”作为第一个参数来构造对象,又该如何呢?好!可以颠倒配置项,如以下代码所示:

 

 

 

 

不过,还有一种方式,那就是像如下代码所示的那样,使用index属性:

 

 

 

 

这时,同样可以得到想要的对象实例,以下控制台输出表明了这一点:

 

..MockBusinessObject@ecd7e[dependency1=22222,dependency2=11111]

注意 index属性的取值从0开始与一般的数组下标取值相同。所以,指定的第一个参数的index应该是0,第二个参数的index应该是1,依此类推。

2. setter方法注入的XML之道

与构造方法注入可以使用注入配置相对应,Spring为setter方法注入提供了元素

有一个name属性(attribute),用来指定该将会注入的对象所对应的实例变量名称。之后通过value或者ref属性或者内嵌的其他元素来指定具体的依赖对象引用或者值,如以下代码所示:

 

 

 

   

 

 

   

 

 

当然,如果只是使用进行依赖注入的话,请确保你的对象提供了默认的构造方法,也就是一个参数都没有的那个。

以上配置形式还可以简化为如下形式:

 

 

 

 

 

使用setter方法注入和使用的构造方法注入并不是水火不容的。实际上,如果需要,可以同时使用这两个元素:

 

 

 

 

当然,现在需要MockBusinessObject提供一个只有一个String类型参数的构造方法,并且为dependency2提供了相应的setter方法。代码清单4-14演示了符合条件的一个业务对象定义。

代码清单4-14 随意声明的一个同时支持构造方法注入和setter方法注入的对象定义

public class MockBusinessObject {

  private String dependency1;

  private String dependency2;

 

  public MockBusinessObject(String dependency)

  {

    this.dependency1 = dependency;

  }

 

  public void setDependency2(String dependency2) {

    this.dependency2 = dependency2;

  }

  ...

}

 

3. 中可用的配置项

之前我们看到,可以通过在这两个元素内部嵌套或者,来指定将为当前对象注入的简单数据类型或者某个对象的引用。不过,为了能够指定多种注入类型,Spring还提供了其他的元素供我们使用,这包括beanref、idrefvalue、nulllist、setmap、props。下面我们来逐个详细讲述它们。

提示 以下涉及的所有内嵌元素对于都是通用的。

(1) 可以通过value为主体对象注入简单的数据类型,不但可以指定String类型的数据,而且可以指定其他Java语言中的原始类型以及它们的包装器(wrapper)类型,比如intInteger等。容器在注入的时候,会做适当的转换工作(我们会在后面揭示转换的奥秘)。你之前已经见过如何使用了,不过让我们通过如下代码来重新认识一下它:

 

  111111

  222222

 

当然,如果愿意,你也可以使用如下的简化形式(不过这里的value是以上一层元素的属性身份出现):

 

 

需要说明的是,是最“底层”的元素,它内部不能再嵌套使用其他元素了。

(2) 使用ref来引用容器中其他的对象实例,可以通过reflocalparentbean属性来指定引用的对象的beanName是什么。代码清单4-15演示了ref及其三个对应属性的使用情况。

代码清单4-15 及其localparentbean属性的使用

constructor-arg>

 

或者

 

或者

 

 

localparentbean的区别在于:

q local只能指定与当前配置的对象在同一个配置文件的对象定义的名称(可以获得XML解析器的id约束验证支持);

q parent则只能指定位于当前容器的父容器中定义的对象引用;

注意 BeanFactory可以分层次通过实现HierarchicalBeanFactory接口),容器A在初始化的时候可以首先加载容器B中的所有对象定义然后再加载自身的对象定义这样容器B就成为了容器A的父容器容器A可以引用容器B中的所有对象定义

BeanFactory parentContainer = new XmlBeanFactory(new ClassPathResource("父容器配置文件路径"));

BeanFactory childContainer  = new XmlBeanFactory(new ClassPathResource("子容器配置文件路径"),parentContainer);

childContainer中定义的对象如果通过parent指定依赖则只能引用parentContainer中的对象定义。

q bean基本上通吃,所以,通常情况下,直接使用bean来指定对象引用就可以了。

的定义为,也就是说,它下面没有其他子元素可用了,别硬往人家肚子里塞东西哦。

(3) 。如果要为当前对象注入所依赖的对象的名称,而不是引用,那么通常情况下,可以使用来达到这个目的,使用如下形式:

 

  djNewsListener

 

但这种场合下,使用idref才是最为合适的。因为使用idref,容器在解析配置的时候就可以帮你检查这个beanName到底是否存在,而不用等到运行时才发现这个beanName对应的对象实例不存在。毕竟,输错名字的问题很常见。以下代码演示了idref的使用:

 

 

这段配置跟上面使用达到了相同的目的,不过更加保险。如果愿意,也可以通过local而不是bean来指定最终值,不过,bean比较大众化哦。

(4) 内部使用可以引用容器中独立定义的对象定义。但有时,可能我们所依赖的对象只有当前一个对象引用,或者某个对象定义我们不想其他对象通过引用到它。这时,我们可以使用内嵌的,将这个私有的对象定义仅局限在当前对象。对于FX新闻系统的DowJonesNews- Listener而言,实际上只有道琼斯的FXNewsProvider会使用它。而且,我们也不想让其他对象引用到它。为此完全可以像代码清单4-16这样,将它配置为内部的形式。

代码清单4-16 内部的配置演示

 

   

   

 

 

   

 

 

这样,该对象实例就只有当前的djNewsProvider可以使用,其他对象无法取得该对象的引用。

注意 因为就只有当前对象引用内部所指定的对象所以,内部id不是必须的当然如果你愿意指定id那也是无所谓的。如下所示

 

 

内部的配置只是在位置上有所差异,但配置项上与其他的是没有任何差别的。也就是说,内嵌的所有元素,内部同样可以使用。如果内部对应的对象还依赖于其他对象,你完全可以像其他独立的定义一样为其配置相关依赖,没有任何差别。

(5) 对应注入对象类型为java.util.List及其子类或者数组类型的依赖对象。通过可以有序地为当前对象注入以collection形式声明的依赖。代码清单4-17给出了一个使用的实例演示。

代码清单4-17 使用进行依赖注入的对象定义以及相关配置内容

public class MockDemoObject

{

  private List param1;

  private String[] param2;

  ...

  // 相应的settergetter方法

  ...

}

 

配置类似于

 

    something

   

   

 

 

    stringValue1

    stringValue2

 

 

注意,元素内部可以嵌套其他元素,并且可以像param1所展示的那样夹杂配置。但是,从好的编程实践来说,这样的处理并不恰当,除非你真的知道自己在做什么!(以上只是出于演示的目的才会如此配置)。

(6) 。如果说可以帮你有序地注入一系列依赖的话,那么就是无序的,而且,对于set来说,元素的顺序本来就是无关紧要的。对应注入Java Collection中类型为java.util. Set或者其子类的依赖对象。代码清单4-18演示了通常情况下的使用场景。

代码清单4-18 使用进行依赖注入的对象定义以及相关配置内容

public class MockDemoObject

{

  private Set valueSet;

  // 必要的settergetter方法

  ...

}

配置类似于

 

    something

   

   

   

      ...

   

 

 

例子就是例子,只是为了给你演示这个元素到底有多少能耐。从配置上来说,这样多层嵌套、多元素混杂配置是完全没有问题的。不过,各位在具体编程实践的时候可要小心了。如果你真的想这么夹杂配置的话,不好意思,估计ClassCastException会很愿意来亲近你,而这跟容器或者配置一点儿关系也没有。

(7) 。与列表(list)使用数字下标来标识元素不同,映射(map)可以通过指定的键(key)来获取相应的值。如果说在中混杂不同元素不是一个好的实践(方式)的话,你就应该求助相同点在于,都是为主体对象注入Collection类型的依赖,不同点在于它对应注入java.util.Map或者其子类类型的依赖对象。代码清单4-19演示了的通常使用场景。

代码清单4-19 使用进行依赖注入的对象定义以及相关配置内容

public class MockDemoObject

{

  private Map mapping;

  // 必要的settergetter方法

  ...

}

配置类似于

 

   

      something

   

   

      objectKey

     

   

   

     

        ...

     

   

    ...

 

 

对于来说,它可以内嵌任意多个,每一个都需要为其指定一个键和一个值,就跟真正的java.util.Map所要求的一样。

q 指定entry的键。可以使用的属性——key或者key-ref来指定键,也可以使用的内嵌元素来指定键,这完全看个人喜好,但两种方式可以达到相同的效果。在内部可以使用以上提到的任何元素来指定键,从简单的到复杂的Collection,只要映射需要,你可以任意发挥。

q 指定entry对应的值。内部可以使用的元素,除了是用来指定键的,其他元素可以任意使用,来指定entry对应的值。除了之前提到的那些元素,还包括马上就要谈到的。如果对应的值只是简单的原始类型或者单一的对象引用,也可以直接使用value或者value-ref这两个属性来指定,从而省却多敲入几个字符的工作量。

注意 key属性用于指定通常的简单类型的键,而key-ref则用于指定对象的引用作为键。

所以,如果你不想敲那么些字符,可以像代码清单4-20所展示的那样使用进行依赖注入的配置。

代码清单4-20 简化版的配置使用演示

public class MockDemoObject

{

  private Map mapping;

  // 必要的settergetter方法

  ...

}

配置类似于

 

   

   

   

     

        ...

     

   

    ...

 

                                                              

(8) 是简化后了的,或者说是特殊化的map,该元素对应配置类型为java.util.Properties的对象依赖。因为Properties只能指定String类型的键(key)和值,所以,的配置简化很多,只有固定的格式,见代码清单4-21

代码清单4-21 使用进行依赖注入的场景演示

public class MockDemoObject

{

  private Properties emailAddrs;

  // 必要的settergetter方法

  ...

}

配置类似于

 

    fujohnwang@gmail.com

    support@spring21.cn

    ...

 

 

每个可以嵌套多个每个通过其key属性来指定键,在内部直接指定其所对应的值。内部没有任何元素可以使用,只能指定字符串,这个是由java.util. Properties的语意决定的。

(9) 最后一个提到的元素是这是最简单的一个元素,因为它只是一个空元素,而且通常使用到它的场景也不是很多。对于String类型来说,如果通过value以这样的方式指定注入,即,那么,得到的结果是"",而不是null。所以,如果需要为这个string对应的值注入null的话,请使用当然,这并非仅限于String类型,如果某个对象也有类似需求,请不要犹豫。代码清单4-22演示了一个使用的简单场景。

代码清单4-22 使用进行依赖注入的简单场景演示

public class MockDemoObject

{

  private String param1;

  private Object param2;

  // 必要的settergetter方法

  ...

}

配置为

 

 

实际上就相当于

public class MockDemoObject

{

  private String param1=null;

  private Object param2=null;

  // 必要的settergetter方法

  ...

}

虽然这里看起来没有太大意义!

 

4. depends-on

通常情况下,可以直接通过之前提到的所有元素,来显式地指定bean之间的依赖关系。这样,容器在初始化当前bean定义的时候,会根据这些元素所标记的依赖关系,首先实例化当前bean定义所依赖的其他bean定义。但是,如果某些时候,我们没有通过类似的元素明确指定对象A依赖于对象B的话,如何让容器在实例化对象A之前首先实例化对象B呢?

考虑以下所示代码:

 

public class SystemConfigurationSetup

{

  static

  {

    DOMConfigurator.configure("配置文件路径");

    // 其他初始化代码

  }

  ...

}        

 

系统中所有需要日志记录的类,都需要在这些类使用之前首先初始化log4j。那么,就会非显式地依赖于SystemConfigurationSetup的静态初始化块。如果ClassA需要使用log4j,那么就必须在bean定义中使用depends-on来要求容器在初始化自身实例之前首先实例化SystemConfigurationSetup,以保证日志系统的可用,如下代码演示的正是这种情况:

 

 

 

log4j在静态代码块(static block)中初始化的例子在实际系统中其实不是很合适,因为通常在应用程序的主入口类初始化日志就可以了。这里主要是给出depends-on可能的使用场景,大部分情况下,是那些拥有静态代码块初始化代码或者数据库驱动注册之类的场景。

如果说ClassA拥有多个类似的非显式依赖关系,那么,你可以在ClassAdepends-on中通过逗号分割各个beanName,如下代码所示:

 

 

 

5. autowire

除了可以通过配置明确指定bean之间的依赖关系,Spirng还提供了根据bean定义的某些特点将相互依赖的某些bean直接自动绑定的功能。通过autowire属性,可以指定当前bean定义采用某种类型的自动绑定模式。这样,你就无需手工明确指定该bean定义相关的依赖关系,从而也可以免去一些手工输入的工作量。

Spring提供了5种自动绑定模式,即nobyNamebyTypeconstructorautodetect,下面是它们的具体介绍。

l no

容器默认的自动绑定模式,也就是不采用任何形式的自动绑定,完全依赖手工明确配置各个bean之间的依赖关系,以下代码演示的两种配置是等效的:

 

 

或者

 

 

l byName

按照类中声明的实例变量的名称,与XML配置文件中声明的bean定义的beanName的值进行匹配,相匹配的bean定义将被自动绑定到当前实例变量上。这种方式对类定义和配置的bean定义有一定的限制。假设我们有如下所示的类定义:

 

public class Foo

{

  private Bar emphasisAttribute;

  ...

  // 相应的setter方法定义

}

public class Bar

{

  ...

}

 

那么应该使用如下代码所演示的自动绑定定义,才能达到预期的目的:

 

 

需要注意两点:第一,我们并没有明确指定fooBean的依赖关系,而仅指定了它的autowire属性为byName;第二,第二个bean定义的idemphasisAttribute,与Foo类中的实例变量名称相同。

l byType

如果指定当前bean定义的autowire模式为byType,那么,容器会根据当前bean定义类型,分析其相应的依赖对象类型,然后到容器所管理的所有bean定义中寻找与依赖对象类型相同的bean定义,然后将找到的符合条件的bean自动绑定到当前bean定义。

对于byName模式中的实例类Foo来说,容器会在其所管理的所有bean定义中寻找类型为Barbean定义。如果找到,则将找到的bean绑定到Foobean定义;如果没有找到,则不做设置。但如果找到多个,容器会告诉你它解决不了“该选用哪一个”的问题,你只好自己查找原因,并自己修正该问题。所以,byType只能保证,在容器中只存在一个符合条件的依赖对象的时候才会发挥最大的作用,如果容器中存在多个相同类型的bean定义,那么,不好意思,采用手动明确配置吧!

指定byType类型的autowire模式与byName没什么差别,只是autowire的值换成byType而已,可以参考如下代码:

 

 

 

l constructor

byNamebyType类型的自动绑定模式是针对property的自动绑定,而constructor类型则是针对构造方法参数的类型而进行的自动绑定,它同样是byType类型的绑定模式。不过,constructor是匹配构造方法的参数类型,而不是实例属性的类型。与byType模式类似,如果找到不止一个符合条件的bean定义,那么,容器会返回错误。使用上也与byType没有太大差别,只不过是应用到需要使用构造方法注入的bean定义之上,代码清单4-23给出了一个使用construtor模式进行自动绑定的简单场景演示。

代码清单4-23 construtor类型自动绑定的使用场景演示

public class Foo

{

  private Bar bar;

  public Foo(Bar arg)

  {

    this.bar = arg;

  }

  ...

}

相应配置为

 

 

l autodetect

这种模式是byTypeconstructor模式的结合体,如果对象拥有默认无参数的构造方法,容器会优先考虑byType的自动绑定模式。否则,会使用constructor模式。当然,如果通过构造方法注入绑定后还有其他属性没有绑定,容器也会使用byType对剩余的对象属性进行自动绑定。

小心 

q       手工明确指定的绑定关系总会覆盖自动绑定模式的行为。

q       自动绑定只应用于“原生类型、String类型以及Classes类型以处”的对象类型,对
 “原生类型、String类型和Classes类型”以及“这些类型的数组”应用自动绑定是无效的

自动绑定与手动明确绑定

自动绑定和手动明确绑定各有利弊。自动绑定的优点有如下两点。

(1) 某种程度上可以有效减少手动敲入配置信息的工作量。

(2) 某些情况下,即使为当前对象增加了新的依赖关系,但只要容器中存在相应的依赖对象,就不需要更改任何配置信息。

自动绑定的缺点有如下几点。

(1) 自动绑定不如明确依赖关系一目了然。我们可以根据明确的依赖关系对整个系统有一个明确的认识,但使用自动绑定的话,就可能需要在类定义以及配置文件之间,甚至各个配置文件之间来回转换以取得相应的信息。

(2) 某些情况下,自动绑定无法满足系统需要,甚至导致系统行为异常或者不可预知。根据类型(byType)匹配进行的自动绑定,如果系统中增加了另一个相同类型的bean定义,那么整个系统就会崩溃;根据名字(byName匹配进行的自动绑定,如果把原来系统中相同名称的bean定义类型给换掉,就会造成问题,而这些可能都是在不经意间发生的。

(3) 使用自动绑定,我们可能无法获得某些工具的良好支持,比如Spring IDE

通常情况下,只要有良好的XML编辑器支持,我不会介意多敲那几个字符。起码自己可以对整个系统的行为有完全的把握。当然,任何事物都不绝对,只要根据相应场景找到合适的就可以。

噢,对了,差点儿忘了!作为所有的统帅,有一个default-autowire属性,它可以帮我们省去为多个单独设置autowire属性的麻烦default-autowire默认值为no,即不进行自动绑定。如果想让系统中所有的定义都使用byType模式的自动绑定,我们可以使用如下配置内容:

 

 

  ...

 

6. dependency-check

我们可以使用每个dependency-check性对其所依赖的对象进行最终检查,就好像电影里每队美国大兵上战场之前,带队的军官都会朝着士兵大喊“检查装备,checkrecheck”是一个道理。该功能主要与自动绑定结合使用,可以保证当自动绑定完成后,最终确认每个对象所依赖的对象是否按照所预期的那样被注入。当然,并不是说不可以与平常的明确绑定方式一起使用。

该功能可以帮我们检查每个对象某种类型的所有依赖是否全部已经注入完成,不过可能无法细化到具体的类型检查。但某些时候,使用setter方法注入就是为了拥有某种可以设置也可以不设置的灵活性,所以,这种依赖检查并非十分有用,尤其是在手动明确绑定依赖关系的情况下。

与军官会让大兵检查枪支弹药和防弹衣等不同装备一样,可以通过dependency-check指定容器帮我们检查某种类型的依赖,基本上有如下4种类型的依赖检查。

q none。不做依赖检查。将dependency-check指定为none跟不指定这个属性等效,所以,还是不要多敲那几个字符了吧。默认情况下,容器以此为默认值。

q simple。如果将dependency-check的值指定为simple,那么容器会对简单属性类型以及相关的collection进行依赖检查,对象引用类型的依赖除外。

q object。只对对象引用类型依赖进行检查。

q all。将simpleobject相结合,也就是说会对简单属性类型以及相应的collection和所有对象引用类型的依赖进行检查。

总地来说,控制得力的话,这个依赖检查的功能我们基本可以不考虑使用。

7. lazy-init

延迟初始化(lazy-init)这个特性的作用,主要是可以针对ApplicationContext容器的bean初始化行为施以更多控制。与BeanFactory不同,ApplicationContext在容器启动的时候,就会马上对所有的“singletonbean定义”进行实例化操作。通常这种默认行为是好的,因为如果系统有问题的话,可以在第一时间发现这些问题,但有时,我们不想某些bean定义在容器启动后就直接实例化,可能出于容器启动时间的考虑,也可能出于其他原因的考虑。总之,我们想改变某个或者某些bean定义在ApplicationContext容器中的默认实例化时机。这时,就可以通过lazy-init属性来控制这种初始化行为,如下代码所示:

 

 

这样,ApplicationContext容器在启动的时候,只会默认实例化not-lazy-init-bean而不会实例化lazy-init-bean

当然,仅指定lazy-init-beanlazy-inittrue,并不意味着容器就一定会延迟初始化该bean的实例。如果某个非延迟初始化的bean定义依赖于lazy-init-bean,那么毫无疑问,按照依赖决计的顺序,容器还是会首先实例化lazy-init-bean,然后再实例化后者,如下代码演示了这种相互牵连导致延迟初始化失败的情况:

 

 

 

   

 

虽然lazy-init-bean是延迟初始化的,但因为依赖它的not-lazy-init-bean并不是延迟初始化,所以lazy-init-bean还是会被提前初始化,延迟初始化的良好打算“泡汤”。如果我们真想保证lazy-init-bean一定会被延迟初始化的话,就需要保证依赖于该bean定义的其他bean定义也同样设置为延迟初始化。在bean定义很多时,好像工作量也不小哦。不过不要忘了,可是所有的统领啊,让它一声令下吧!如代码清单4-24所演示的,在顶层由统一控制延迟初始化行为即可。

代码清单4-24 通过设置统一的延迟初始化行为

 

 

 

   

     

   

 

  ...

这样我们就不用每个都设置一遍,省事儿多了不是吗?
阅读(969) | 评论(0) | 转发(0) |
0

上一篇:spring

下一篇:spring 2

给主人留下些什么吧!~~