Spring | IOC之bean基于XML的注入(装配)
前面讲到基于XML的bean的 三种实例化,这篇文章来学习下基于XML如何构建bean与bean之间的关系以及如何实现依赖关系的注入。
1 依赖关系的理解
如何理解依赖,在代码中是怎么体现的?
假设有两个元素X、Y,如果修改元素X的定义可能会引起对另一个元素Y的修改,则称元素Y依赖于元素X。在OOP对象中,元素为类或者应用,则类间的依赖现象有多种:
- 如一个类向另一个类发消息;这是类与类之间交流的方式,可以理解为依赖关系的总称。
- 一个类是另一个类的数据成员;
public class B { private A a; // B依赖A }
- 一个类是另一个类的某个方法的参数。
public class B { void method(A a){ // B依赖A // doSomething; } }
关于更多类之间的关系,可查看参考资料。
2 bean间的依赖
bean间的依赖在IOC容器中怎么体现?注意循环依赖问题。
传统对象间依赖的实现,都是依赖的对象或者资源在被依赖类内部进行实例化或者初始化,而Spring则把这种依赖的实现(即装配)放入IOC容器中,由IOC容器来负责实例化bean或者所依赖的资源。之前讲过注入的含义,那么这里出现了“装配”,两者之前有什么关联?我们后续回讲到。
3 bean的依赖注入
既然已经知道bean之间的依赖关系放到IOC容器中建立,且通过注入的方式来实现bean间的依赖,那么注入又是如何实现的呢?在基于XML实现的注入方式中,分为两种:构造器注入和setter方法注入。下面分别进行说明。
3.1 构造器注入
概念:构造器注入顾名思义是通过参数构造器进行注入,把依赖的对象或者资源通过构造器参数注入到被依赖对象中,实现两者的依赖关系。
3.1.1 实例1:含参构造器注入
创建ElectronicProducts接口:
package com.starry.bean.xml.constructorInjection;
public interface ElectronicProducts {
void doSomething();
}
创建Computer类:
package com.starry.bean.xml.constructorInjection;
public class Computer implements ElectronicProducts {
private Mouse mouse;
public Computer(Mouse mouse) {
this.mouse = mouse;
}
@Override
public void doSomething() {
System.out.println("computer...");
mouse.doSomething();
}
}
PS:实现ElectronicProducts接口,且包含一个带参数的构造函数。
Mouse类很简单,里面有个doSomething()方法:
package com.starry.bean.xml.constructorInjection;
public class Mouse {
void doSomething(){
System.out.println("mouse...");
}
}
上面是所有的接口和类,重点在Computer类里面,要想让Computer类的实例doSomething()方法正确执行,则必须要实例化Mouse类生成mouse对象;目前代码中只是建立一个表面的Computer类依赖Mouse类的关系,为什么说是表面的,因为编译并不报错,而运行必然报错。Spring把这种依赖放到IOC容器中,具体体现在XML配置文件中:
<bean id="mouse" class="com.starry.bean.xml.constructorInjection.Mouse" />
<bean id="computer" class="com.starry.bean.xml.constructorInjection.Computer">
<constructor-arg ref="mouse" />
</bean>
XML配置中通过bean标签生成两个bean对象(mouse和computer);通过constructor-arg标签把mouse对象注入到computer对象中。
xml文件执行过程:当IOC容器遇到第一个bean元素时,它会创建id为mouse的bean对象;当IOC容器遇到第二个bean元素时,它会创建一个computer对象,同时constructor-arg元素会告知IOC容器要将一个id为mouse的bean引用传递到Computer的构造器中。这里可以思考下调换两个bean实例化的顺序会发生什么?
注意:测试Test类在源码中,可下载执行测试。
还有一种替代构造器注入的方案,即Spring的c-命名空间,这种方案是在3.0中引入的,它之所以能够成为一种替代方案,是因为其比上面构造器注入简洁。
3.1.2 实例2:c-命名空间
首先在xml文件顶部声明其模式:(新增)
xmlns:c="http://www.springframework.org/schema/c"
上述第二个bean声明改为:
<bean id="computer" class="com.starry.bean.xml.constructorInjection.Computer" c:mouse-ref="mouse" />
- c:mouse-rsf中,c是constructor的首字母;
- mouse是构造器参数名称;
- -ref表示注入的是对象的引用;
- mouse是注入bean实例的id。
注意:也可以使用参数在参数列表中的位置信息来代替参数名称:c:_0-ref=”mouse”(xml中不允许数字开头的标识符,所以以下划线作前缀)
注意:测试Test类在源码中,可下载执行测试。
到这里你可能发现上面注入的都是对象引用,而实际情况还会注入字符串值、整形值以及集合。
3.1.3 实例3:注入字符串和整形
给电脑添加品牌(字符串类型)和价格(整形类型)
public class Computer implements ElectronicProducts {
// 实例1
private Mouse mouse;
// 实例3
private String name;
private int pri·ce;
public Computer(Mouse mouse, String name, int price) {
this.mouse = mouse;
this.name = name;
this.price = price;
}
@Override
public void doSomething() {
// 实例1
//System.out.println("computer...");
//mouse.doSomething();
// 实例3
System.out.println("computer... 品牌:" + name + ", 价格:" + price );
mouse.doSomething();
}
}
xml配置文件constructorInjection.xml修改为:
<bean id="mouse" class="com.starry.bean.xml.constructorInjection.Mouse" />
<bean id="computer" class="com.starry.bean.xml.constructorInjection.Computer">
<constructor-arg ref="mouse" />
<constructor-arg name="name" value="HP"/>
<constructor-arg name="price" value="5000"/>
</bean>
没错,这个跟上篇“bean的实例化”类似,可以通过name、type和index注入字符串和整形类型。
如果要用c-命名空间注入字符串和整形,可用如下两种方式:
<bean id="computer" class="com.starry.bean.xml.constructorInjection.Computer"
c:_0-ref="mouse"
c:_1="hp"
c:_2="5000"
/>
<bean id="computer" class="com.starry.bean.xml.constructorInjection.Computer"
c:mouse-ref="mouse"
c:name="hp"
c:price="5000" />
注意:可以思考下当有多个含参构造器时,c-命名空间形式改如何写?
3.1.4 实例4:注入集合
给电脑添加多个CPU,修改如下:
<bean id="computer" class="com.starry.bean.xml.constructorInjection.Computer">
<constructor-arg ref="mouse" />
<constructor-arg name="name" value="HP"/>
<constructor-arg name="price" value="5000"/>
<constructor-arg>
<list>
<value>cpu1</value>
<value>cpu2</value>
<value>cpu3</value>
</list>
</constructor-arg>
</bean>
注意:注入set也一样,把list标签换成set标签。
如果是引用列表,则改成:
<bean id="computer" class="com.starry.bean.xml.constructorInjection.Computer">
<constructor-arg ref="mouse" />
<constructor-arg name="name" value="HP"/>
<constructor-arg name="price" value="5000"/>
<constructor-arg>
<list>
<ref bean="cpu1"/>
<ref bean="cpu2"/>
<ref bean="cpu3"/>
</list>
</constructor-arg>
</bean>
注意:c-命名空间的形式无法实现集合的注入。
3.2 setter方法注入
概念:setter方法注入是指通过setter方法把依赖的对象注入到某对象中,也经常被称为属性注入。
3.2.1 实例5:setter注入
修改Computer类,其他类保持不变:
import com.starry.bean.xml.constructorInjection.ElectronicProducts;
import com.starry.bean.xml.constructorInjection.Mouse;
public class Computer implements ElectronicProducts {
private Mouse mouse;
public void setMouse(Mouse mouse) {
this.mouse = mouse;
}
@Override
public void doSomething() {
System.out.println("computer...");
mouse.doSomething();
}
}
很明显,用setMouse()方法代替了原来的带参构造函数。
新建xml配置文件setterInjection.xml,内容如下:
<bean id="mouse" class="com.starry.bean.xml.constructorInjection.Mouse" />
<!-- 实例1-->
<bean id="computer" class="com.starry.bean.xml.setterInjection.Computer">
<property name="mouse" ref="mouse" />
</bean>
用元素代替了元素,其注入是通过setMouse()方法完成的。
构造函数注入可以用c-命名空间代替,setter方法注入可用p-命名空间代替。
3.2.2 实例6:p-命名空间
首先在xml头文件中进行p标签声明:
xmlns:p="http://www.springframework.org/schema/p"
在xml文件中配置p标签:
<bean id="computer" class="com.starry.bean.xml.setterInjection.Computer" p:mouse-ref="mouse" />
p:mouse-ref=”mouse”
- 其中p表示property首字母,表明我们所设置的是一个属性;
- mouse表示要注入的属性名;
- -ref表示注入的是一个对象;
- 最后的mouse表示要注入对象的id,而不是普通的字面量。
注意:测试Test类在源码中,可自行下载测试。
下面简单说一下对字面量,即字符串、整形和集合的注入。
3.2.3 实例7:setter注入字符串、整形和集合
<bean id="computer" class="com.starry.bean.xml.setterInjection.Computer">
<property name="mouse" ref="mouse" />
<property name="name" value="hp" />
<property name="price" value="5000" />
<property name="cpuList">
<list>
<value>cpu1</value>
<value>cpu2</value>
<value>cpu3</value>
</list>
</property>
</bean>
通过p标签注入如下:
<bean id="computer" class="com.starry.bean.xml.setterInjection.Computer"
p:mouse-ref="mouse"
p:name="hp"
p:price="5000">
<property name="cpuList">
<list>
<value>cpu1</value>
<value>cpu2</value>
<value>cpu3</value>
</list>
</property>
</bean>
可见p标签虽然实现集合注入,但是并没有使用p标签的特性,而是按照setter的方式注入集合。
虽然不能使用p-命名空间来注入集合,但是可以通过Spring提供的util-命名空间方式来简化集合注入。
3.2.4 实例8:util-命名空间
首先在xml文件头声明util-标签:
xmlns:util=http://www.springframework.org/schema/util
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
xml配置文件修改如图所示:
<util:list id="cpuListTemp">
<value>cpu1</value>
<value>cpu2</value>
<value>cpu3</value>
</util:list>
<bean id="computer" class="com.starry.bean.xml.setterInjection.Computer"
p:mouse-ref="mouse"
p:name="hp"
p:price="5000"
p:cpuList-ref="cpuListTemp"
/>
可见把集合通过util:list元素当做一个单独的实例,在实际使用的bean中作为引用注入。
注意:util-提供其他多种集合的注入途径,具体可参考下表:
元素 | 描述 |
---|---|
util:constant | 某个类型的public static域,并将其暴露为bean |
util:list | 建一个java.util.List类型的bean,其中包含值或引用 |
util:map | 建一个java.util.Map类型的bean,其中包含值或引用 |
util:property | 建一个java.util.Property型的bean |
util:property-path | 引用一个bean的属性,并将其暴露为bean |
util:set | 创建一个java.util.Set类型的bean,其中包含值或引用 |
4 总结
我们学习了以下几点内容:
-
依赖关系的理解;
-
基于xml,依赖注入的两种方式:带参构造函数注入和setter方法注入;
-
带参构造函数注入和c-命名空间注入;(引用、字符串、整形和集合)
-
setter方法注入和p-命名空间注入;(util-标签的使用)
5 参考资料
类之间关系资料:http://www.uml.org.cn/oobject/201211231.asp
《Spring实战4》