spring框架setter注入和构造器constructor注入中的内部嵌套

先来看看两种注入方式的区别

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

按照Spring的IoC容器配置格式,要通过构造方法注入方式,为当前业务对象注入其所依赖的对象,
需要使 用<constructor-arg>。正常情况下,如以下代码所示:
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg>
<ref bean="djNewsListener"/>
</constructor-arg>
<constructor-arg>
<ref bean="djNewsPersister"/>
</constructor-arg>
</bean>
对于<ref>元素,稍后会进行详细说明。这里你只需要知道,通过这个元素来指明容器将为djNewsProvider这个<bean>注入通过<ref>所引用的Bean实例。这种方式可能看起来或者编写起来
不是很 简洁,最新版本的Spring也支持配置简写形式,如以下代码所示:
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg ref="djNewsListener"/>
<constructor-arg ref="djNewsPersister"/>
</bean>
简洁多了不是嘛?其实,无非就是表达方式不同而已,实际达到的效果是一样的。
有些时候,容器在加载XML配置的时候,因为某些原因,无法明确配置项与对象的构造方法参数
列表的一一对应关系,就需要请<constructor-arg>的type或者index属性出马。比如,对象存在多个构造方法,当参数列表数目相同而类型不同的时候,容器无法区分应该使用哪个构造方法来实例化
对象,或者构造方法可能同时传入最少两个类型相同的对象。

 type属性
假设有一个对象定义如代码清单4-12所示。 2
代码清单4-12 随意声明的一个业务对象定义
public class MockBusinessObject { 3
private String dependency1;
private int dependency2;
4
public MockBusinessObject(String dependency)
{
this.dependency1 = dependency; } 5
public MockBusinessObject(int dependency)
{ 6
this.dependency2 = dependency;
}
... 7
@Override
public String toString() {
return new ToStringBuilder(this) ➥ .append("dependency1", dependency1) ➥ 8
.append("dependency2", dependency2).toString();
}
} 9
该类声明了两个构造方法,分别都只是传入一个参数,且参数类型不同。这时,我们可以进行配
置,如以下代码所示: 10
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg>
<value>111111</value> </constructor-arg> 11
< /bean>
如果从BeanFactory取得该对象并调用toString()查看的话,我们会发现Spring调用的是第一个
构造方法,因为输出是如下内容: 12
. .MockBusinessObject@f73c1[dependency1=111111,dependency2=0]
13
14
但是,如果我们想调用的却是第二个传入int类型参数的构造方法,又该如何呢?可以使用type属性,通 过指定构造方法的参数类型来解决这一问题,配置内容如下代码所示:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg type="int">
<value>111111</value>
</constructor-arg> </bean> 15
现 在,我们得到了自己想要的对象实例,如下的控制台输出信息印证了这一点:
..MockBusinessObject@f73c1[dependency1=<null>,dependency2=111111] 16
 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();
}
}
并且,配置内容如以下代码所示:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg value="11111"/>
<constructor-arg value="22222"/>
</bean>
那么,我们可以得到如下对象:
..MockBusinessObject@1ef8cf3[dependency1=11111,dependency2=22222]
但是,如果要让“11111”作为对象的第二个参数,而将“22222”作为第一个参数来构造对象,
又该如何呢?好!可以颠倒配置项,如以下代码所示:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg value="22222"/>
<constructor-arg value="11111"/>
</bean>
不过,还有一种方式,那就是像如下代码所示的那样,使用index属性:
<bean id="mockBO" class="..MockBusinessObject">
<constructor-arg index="1" value="11111"/>
<constructor-arg index="0" value="22222"/>
</bean>
这时,同样可以得到想要的对象实例,以下控制台输出表明了这一点:
..MockBusinessObject@ecd7e[dependency1=22222,dependency2=11111]
注意 index属性的取值从0开始,与一般的数组下标取值相同。所以,指定的第一个参数的index
应该是0,第二个参数的index应该是1,依此类推。

2. setter方法注入的XML之道

与构造方法注入可以使用<constructor-arg>注入配置相对应,Spring为setter方法注入提供了<property>元素。

<property>有一个name属性(attribute),用来指定该<property>将会注入的对象所对应的实例
变量名称。之后通过value或者ref属性或者内嵌的其他元素来指定具体的依赖对象引用或者值,如以
下代码 所示: <bean id="djNewsProvider" class="..FXNewsProvider"> 2
<property name="newsListener">
<ref bean="djNewsListener"/>
</property> 3
<property name="newPersistener">
<ref bean="djNewsPersister"/>
</property> </bean> 4
当然,如果只是使用<property>进行依赖注入的话,请确保你的对象提供了默认的构造方法,
也就是一个参数都没有的那个。 5
以 上配置形式还可以简化为如下形式:
<bean id="djNewsProvider" class="..FXNewsProvider"> <property name="newsListener" ref="djNewsListener"/> 6
<property name="newPersistener" ref="djNewsPersister"/>
< /bean>
使用<property>的setter方法注入和使用<constructor-arg>的构造方法注入并不是水火不容7的。实 际上,如果需要,可以同时使用这两个元素:
<bean id="mockBO" class="..MockBusinessObject"> 8
<constructor-arg value="11111"/>
<property name="dependency2" value="22222"/>
< /bean> 9 当然,现在需要MockBusinessObject提供一个只有一个String类型参数的构造方法,并且为dependency2提供了相应的setter方法。代码清单4-14演示了符合条件的一个业务对象定义。
10 代码清单4-14 随意声明的一个同时支持构造方法注入和setter方法注入的对象定义
public class MockBusinessObject {
private String dependency1; 11
private String dependency2;
public MockBusinessObject(String dependency) { 12
this.dependency1 = dependency;
}
13
14
public void setDependency2(String dependency2) {
this.dependency2 = dependency2;
}
...
}
3. <property>和<constructor-arg>中可用的配置项 15 之前我们看到,可以通过在<property>和<constructor-arg>这两个元素内部嵌套<value>或者
<ref>,来指定将为当前对象注入的简单数据类型或者某个对象的引用。不过,为了能够指定多种注
入类型,Spring还提供了其他的元素供我们使用,这包括bean、ref、idref、value、null、list、set、map、
props。下面我们来逐个详细讲述它们。

提示 以下涉及的所有内嵌元素,对于<property>和<constructor-arg>都是通用的。
(1) <value>。可以通过value为主体对象注入简单的数据类型,不但可以指定String类型的数据,
而且可以指定其他Java语言中的原始类型以及它们的包装器(wrapper)类型,比如int、Integer等。
容器在注入的时候,会做适当的转换工作(我们会在后面揭示转换的奥秘)。你之前已经见过如何使
用<v alue>了,不过让我们通过如下代码来重新认识一下它:
<constructor-arg>
<value>111111</value>
</constructor-arg>
<property name="attributeName">
<value>222222</value>
</property>
当然,如果愿意,你也可以使用如下的简化形式(不过这里的value是以上一层元素的属性身份
出现) :
<constructor-arg value="111111"/>
<property name="attributeName" value="222222"/>
需要说明的是,<value>是最“底层”的元素,它内部不能再嵌套使用其他元素了。
(2) <ref>。使用ref来引用容器中其他的对象实例,可以通过ref的local、parent和bean属性来
指定引用的对象的beanName是什么。代码清单4-15演示了ref及其三个对应属性的使用情况。
代码清单4-15 <ref>及其local、parent和bean属性的使用
constructor-arg>
<ref local="djNewsPersister"/>
</constructor-arg>
或者
<constructor-arg>
<ref parent="djNewsPersister"/>
</constructor-arg>
或者
<constructor-arg>
<ref bean="djNewsPersister"/>
</constructor-arg>
local、parent和bean的区别在于:
 local只能指定与当前配置的对象在同一个配置文件的对象定义的名称(可以获得XML解析器的id约束验证支持);
 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中
的对象定义。

 bean则基本上通吃,所以,通常情况下,直接使用bean来指定对象引用就可以了。
<ref>的定义为<!ELEMENT ref EMPTY>,也就是说,它下面没有其他子元素可用了,别硬往人家
肚子里塞东西哦。
(3) <idref>。如果要为当前对象注入所依赖的对象的名称,而不是引用,那么通常情况下,可以使用<value>来达到这个目的,使用如下形式:
<property name="newsListenerBeanName"> 
<value>djNewsListener</value>
< /property>
但这种场合下,使用idref才是最为合适的。因为使用idref,容器在解析配置的时候就可以帮
你检查这个beanName到底是否存在,而不用等到运行时才发现这个beanName对应的对象实例不存在。
毕竟,输错名字的问题很常见。以下代码演示了idref的使用: 5
<property name="newsListenerBeanName">
<idref bean="djNewsListener"/>
</property> 6
这段配置跟上面使用<value>达到了相同的目的,不过更加保险。如果愿意,也可以通过local而
不是bean来指定最终值,不过,bean比较大众化哦。 (4) 内部<bean>。使用<ref>可以引用容器中独立定义的对象定义。但有时,可能我们所依赖的对7
象只有当前一个对象引用,或者某个对象定义我们不想其他对象通过<ref>引用到它。这时,我们可
以使用内嵌的<bean>,将这个私有的对象定义仅局限在当前对象。对于FX新闻系统的DowJonesNews-
Listener而言,实际上只有道琼斯的FXNewsProvider会使用它。而且,我们也不想让其他对象引用
到它。为此完全可以像代码清单4-16这样,将它配置为内部<bean>的形式。
8
9
代码清单4-16 内部<bean>的配置演示
<bean id="djNewsProvider" class="..FXNewsProvider"> 10
<constructor-arg index="0">
<bean class="..impl.DowJonesNewsListener">
</bean> </constructor-arg> 11
<constructor-arg index="1">
<ref bean="djNewsPersister"/>
</constructor-arg>
< /bean>
这样,该对象实例就只有当前的djNewsProvider可以使用,其他对象无法取得该对象的引用。

注意 因为就只有当前对象引用内部<bean>所指定的对象,所以,内部<bean>的id不是必须
的。当然,如果你愿意指定id,那也是无所谓的。如下所示:
<constructor-arg index="0">
<bean id="djNewsListener" class="..impl.DowJonesNewsListener">
</bean> </constructor-arg> 15
内部<bean>的配置只是在位置上有所差异,但配置项上与其他的<bean>是没有任何差别的。也
就是说,<bean>内嵌的所有元素,内部<bean>的<bean>同样可以使用。如果内部<bean>对应的对象
还依赖于其他对象,你完全可以像其他独立的<bean>定义一样为其配置相关依赖,没有任何差别。
(5) <list>。<list>对应注入对象类型为java.util.List及其子类或者数组类型的依赖对象。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值