您的位置:首页 > 编程语言 > Java开发

Spring 核心技术——IoC 之 DI (1)

2017-03-18 14:51 453 查看
DI 简介

DI 的方式
1 设值注入
JavaBean

applicationContextxml

测试方法

测试结果

2 构造器注入
JavaBean

applicationContextxml

测试方法

测试结果

DI 的类型
1 其他 bean
11 idref 元素

12 ref 元素
121 ref bean

122 ref local

123 ref parent

2 内部 bean

3 集合
31 常规使用

32 集合整合

33 强类型集合

4 Null 或空字符串
41 空字符串

42 Null

5 复合嵌套属性名

1 DI 简介

DI(Dependency Injection)

即依赖注入,提供普通的Java方法让容器去决定依赖关系。容器全权负责的组件的装配,它会把符合依赖关系的对象通过 JavaBean 属性或者构造函数传递给需要的对象。

注意:通常,我们会将 IoC 与 DI 作为同一个概念理解,其实 IoC 是一个很大的概念,DI 只是其中一个比较流行的实现策略。只是对于 Spring 而言,其 IoC 容器就是通过 DI 实现的,所以也就不再严格区分。

2 DI 的方式

在 Spring 中,主要有以下两种方式:

设值注入:通过JavaBean属性注射依赖关系

构造器注入:将依赖关系作为构造函数参数传入

2.1 设值注入

JavaBean

package com.jyhuang.spring.DI.setter_based;

public class People {
private String name;
private int age;

// 使用设值注入,必须提供对应属性的 setter 方法
// getter...setter

@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}


applicationContext.xml

使用
<property>
标签进行属性的注入,其中标签属性
name
指定 JavaBean 中对应的属性名,标签属性
value
指定需要注入的值,这两个标签都是必须的。

<bean id="people"  class="com.jyhuang.spring.DI.setter_based.People">
<property name="name" value="Tom"/>
<property name="age" value="20"/>
</bean>


测试方法

@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"com/jyhuang/spring/DI/setter_based/applicationContext.xml");
People people = (People) ctx.getBean("people");
System.out.println(people.toString());
}


测试结果

People{name='Tom', age=20}


2.2 构造器注入

JavaBean

package com.jyhuang.spring.DI.constructor_based;

public class People {
private String name;
private int age;

public People() {}

// 使用构造器注入,必须提供以对应属性为参数的构造方法
public People(String name, int age) {
this.name = name;
this.age = age;
}

// getter...setter

@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}


applicationContext.xml

使用
<constructor-arg>
标签进行构造器注入。

其中,除了可以通过最常用的标签属性
name
value
进行注入外,
index
type
属性同样可以,这两个属性分别对应构造器中传参的索引和参数类型。三个属性可单独或组合使用,但必须保证最终能精确定位到需要注入值的那个参数。

<property
不同,此标签的
name
属性不是必须的。默认情况下,会根据参数类型自动注入对应的属性,只需要指定
value
即可,即使配置的顺序与构造器的传参顺序不一致,一样能正确注入,但前提是能通过参数类型唯一定位;如果参数类型一致,Spring 会根据配置顺序依次注入。为了避免麻烦,建议配置时手动定位。

<bean id="people"  class="com.jyhuang.spring.DI.constructor_based.People">
<!--<constructor-arg name="name" value="Tom"/>-->
<!--<constructor-arg name="age" value="20"/>-->

<constructor-arg index="0" type="java.lang.String" name="name" value="Tom"/>
<constructor-arg index="1" type="int" name="age" value="20"/>
</bean>


测试方法

@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"com/jyhuang/spring/DI/constructor_based/applicationContext.xml");

People p = (People) ctx.getBean("people");
System.out.println(p);
}


测试结果

fc4c

People{name='Tom', age=20}


3 DI 的类型

除了之前例子中提及到的基本数据类型和 String 等直接值以外,依赖注入的类型还有很多,下面作简要说明。

3.1 其他 bean

注入其他 JavaBean,也叫合作 bean 或者协同 bean,主要有以下两种方式:
idref
ref
,这两个元素都可以作为
<constructor-arg>
<property>
标签的子元素来指定需要注入的 bean。

3.1.1 idref 元素

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

<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean" />
</property>
</bean>


上面的配置,在运行时完全等价于下面的配置

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

<bean id="client" class="...">
<property name="targetName" value="theTargetBean" />
</bean>


idref bean
property value
注入的值都是字符串类型,只是
idref
注入的是容器中已经存在的 bean 的 id 值,该元素在容器发布时,会对注入的值进行校验,判断容器中是否真的存在这样一个 bean,该 bean 的 id 与
idref bean
配置值相同,而
property value
则会直接把值作为字符串注入。

注意:
idref
注入的是字符串类型的值,只是这个值必须对应某个 bean 的 id 值,它并不是真地注入一个 bean,所以,接收该值的属性也必须是字符串。


另外,在 4.0 版本之前,
idref
有个
local
属性,但从 4.0 的 beans xsd 开始已经不再被支持。当升级版本时,只需要简单地将
idref local
修改成
idref bean
即可。

3.1.2 ref 元素

idref
元素不同,
ref
元素注入的是 bean 实例,而且,
ref bean
的值不仅可以是其他 bean 的 id 属性值,还可以是 name 属性值。

3.1.2.1
ref bean

<ref bean="someBean"/>


3.1.2.2
ref local

这个和
idref
一样,从 4.0 的 beans xsd 开始,Spring 就不再支持,此处不再说明。

3.1.2.3
ref parent

通过使用
ref parent
来引用当前容器的父容器中的 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" <!-- bean name is the same as 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 here -->
</bean>


另外,
ref
不仅可以作为
<constructor-arg>
<property>
的子标签,还可以作为属性进行 bean 的注入,此时,就等价于
ref bean


<property name="target" ref="someBean"/>


3.2 内部 bean

使用
<bean/>
标签在
<constructor-arg>
<property>
中定义内部 bean。此时,无须为该内部 bean 指定 id/name 属性,即使指定,容器也会忽略,因为该内部 bean 只能给它对应的外部 bean 使用。另外,对于内部 bean 的 scope 属性,容器也会忽略,因为内部 bean 永远都是匿名的,且随着外部 bean 的创建而创建。

<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>


3.3 集合

Spring 主要提供了如下类型的集合注入

List:
<list/>


Set:
<set/>


Map:
<map/>


Properties:
<props/>


3.3.1 常规使用

<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</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="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</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


3.3.2 集合整合

集合整合实际就是集合继承,Spring 是支持一个应用定义父类型的集合元素,然后使用子类型的集合元素去继承和覆盖父类型集合元素中的值,只需要在子集合定义时显示的指定
merge=true
,适用于上述列举的四种集合类型。

<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>


注意:在整合中,如果父子集合类型不一致,是不能整合的。

3.3.3 强类型集合

从 Java 5 开始引入了泛型,所以,对于指定了元素类型的集合注入,需要注意类型匹配问题。由于 Spring 自己提供了基本类型转换支持,所以,默认情况下,Spring 会根据目标类型对注入的值进行自动转换。

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>


此例中,当注入
9.99
2.75
3.99
时,都会自动转换成 Float 类型。

3.4 Null 或空字符串

3.4.1 空字符串

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


上面的配置等价于

exampleBean.setEmail("")


3.4.2 Null

null 值的注入比较特殊,需要使用
<null/>
元素来完成。

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


上面的配置等价于

exampleBean.setEmail(null)


3.5 复合/嵌套属性名

Spring 支持
对象.属性
这种嵌套的方式进行属性值的注入,但必须保证在嵌套过程的对象/属性必须存在且不能为 null,否则,会抛出异常。

<bean id="foo" class="foo.Bar">
<property name="fred.bob.sammy" value="123" />
</bean>


上例中,
Bar
必须有
fred
对象作为属性,
fred
对象必须有
bob
对象作为属性,
bob
对象必须有
sammy
对象作为属性;在注入
123
之前,
Bar
fred
bob
对象必须已经初始化,不能为 null,否则会抛出
NullPointerException
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring4 IoC DI