Spring学习笔记(6)——IoC的三种注入方式
- 1.接口注入(不推荐)
- 2.构造器注入(死的应用)
- 3.getter,setter方式注入(比较常用)
Type1 接口注入
我们常常借助接口来将调用者与实现者分离。如:
- public class ClassA {
- private InterfaceB clzB;
- public init() {
- Ojbect obj =
- Class.forName(Config.BImplementation).newInstance();
- clzB = (InterfaceB)obj;
- }
- ……
- }
上面的代码中,ClassA依赖于InterfaceB的实现,如何获得InterfaceB实现类的实例?传统的方法是在代码中创建InterfaceB实现类的实例,并将起赋予clzB。
而这样一来,ClassA在编译期即依赖于InterfaceB的实现。为了将调用者与实现者在编译期分离,于是有了上面的代码,我们根据预先在配置文件中设定的实现类的类名,动态加载实现类,并通过InterfaceB强制转型后为ClassA所用。
这就是接口注入的一个最原始的雏形。
而对于一个Type1型IOC容器而言,加载接口实现并创建其实例的工作由容器完成,如J2EE开发中常用的Context.lookup(ServletContext.getXXX),都是Type1型IOC的表现形式。
Apache Avalon是一个典型的Type1型IOC容器。
Type2 构造子注入
构造子注入,即通过构造函数完成依赖关系的设定,如:
- public class DIByConstructor {
- private final DataSource dataSource;
- private final String message;
- public DIByConstructor(DataSource ds, String msg) {
- this.dataSource = ds;
- this.message = msg;
- }
- ……
- }
可以看到,在Type2类型的依赖注入机制中,依赖关系是通过类构造函数建立,容器通过调用类的构造方法,将其所需的依赖关系注入其中。
PicoContainer(另一种实现了依赖注入模式的轻量级容器)首先实现了Type2类型的依赖注入模式。
Type3 设值注入
在各种类型的依赖注入模式中,设值注入模式在实际开发中得到了最广泛的应用(其中很大一部分得力于Spring框架的影响)。
在笔者看来,基于设置模式的依赖注入机制更加直观、也更加自然。Quick Start中的示例,就是典型的设置注入,即通过类的setter方法完成依赖关系的设置。
几种依赖注入模式的对比总结
接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。
Type2和Type3的依赖注入实现模式均具备无侵入性的特点。在笔者看来,这两种实现方式各有特点,也各具优势(一句经典废话?)。
Type2 构造子注入的优势:
- “在构造期即创建一个完整、合法的对象”,对于这条Java设计原则,Type2无疑是最好的响应者。
- 避免了繁琐的setter方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,更加易读。
- 由于没有setter方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处相对“不变”的稳定状态,无需担心上层代码在调用过程中执行setter方法对组件依赖关系产生破坏,特别是对于Singleton模式的组件而言,这可能对整个系统产生重大的影响。
- 同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的层次清晰性提供了保证。
- 通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要,比如某个依赖关系注入的先决条件是组件的DataSource及相关资源已经被设定。
Type3 设值注入的优势:
- 对于习惯了传统JavaBean开发的程序员而言,通过setter方法设定依赖关系显得更加直观,更加自然。
- 如果依赖关系(或继承关系)较为复杂,那么Type2模式的构造函数也会相当庞大(我们需要在构造函数中设定所有依赖关系),此时Type3模式往往更为简洁。
- 对于某些第三方类库而言,可能要求我们的组件必须提供一个默认的构造函数(如Struts中的Action),此时Type2类型的依赖注入机制就体现出其局限性,难以完成我们期望的功能。
可见,Type2和Type3模式各有千秋,而Spring、PicoContainer都对Type2和Type3类型的依赖注入机制提供了良好支持。这也就为我们提供了更多的选择余地。理论上,以Type2类型为主,辅之以Type3类型机制作为补充,可以达到最好的依赖注入效果,不过对于基于Spring Framework开发的应用而言,Type3使用更加广泛。借鉴http://blog.csdn.net/zhuangzhineng/article/details/3967234
一、构造器注入
构造器注入顾名思义就是在程序组件中实现构造器,构造器可以是一个也可以是多个。废话不多说,直接上代码。
- package cn.glzaction.service.impl;
- import java.util.List;
- import cn.glzaction.service.interfaces.PersonDaoIF;
- import cn.glzaction.service.interfaces.PersonServiceIF;
- public class PersonServiceBean implements PersonServiceIF{
- //自定义类
- private PersonDaoIF personDaoBean;
- //String类型
- private String name;
- //集合类型
- private List list;
- //构造器
- public PersonServiceBean(PersonDaoBean personDaoBean,String name,List list){
- this.personDaoBean = personDaoBean;
- this.name = name;
- this.list = list;
- }
- //方法,用于显示
- public void display(){
- personDaoBean.add();
- System.out.println(name);
- System.out.println(list);
- }
- }
上面的代码中构造器要注入三个参数,同时这三个参数也是三种不同的类型,自定义类、String类型,集合类型,其中自定义类PersonDaoBean 具体的实现我们这里就不累述了,因为这不是重点。下面我们再来看一下在Spring的配置文件中如何来配置。
- <bean id="personDao" class="cn.glzaction.service.impl.PersonDaoBean"></bean >
- <!--构造器方式注入-->
- <bean id="personService" class="cn.glzaction.service.impl.PersonServiceBean">
- <constructor-arg index="0" type="cn.glzaction.service.impl.PersonDaoBean" ref="personDao"/>
- <constructor-arg index="1" type="java.lang.String" value="glzaction"/>
- <constructor-arg index="2" type="java.util.List">
- <list>
- <value>list1</value>
- <value>list2</value>
- <value>list3</value>
- </list>
- </constructor-arg>
- </bean>
java代码如何实现构造器我想大家一定都很清楚,否则的话就有点讲不过去了,呵呵,所以这里我也就不加以说明了,主要说一下上面的xml配置。id为“personDao”的bean是PersonServiceBean的私有属性,它的注入是采用无参构造器的注入方式注入的,这也不详细说明。主要说一下id为“personService”类的构造器注入。<coustructor-arg>是构造器标签元素,通过设定它的属性可以往构造器传递参数,index属性值表示要设定的参数在构造器形参中的索引顺序,例如上面的配置,list是第三个参数,所以它对应的索引为2,index是可选属性,所谓可选并不是说在任何情况下都可以不使用,要视具体情况而定,type为参数的类型,这个也是可选参数。还有两个很重要的属性就是ref和value,如果注入的是bean,就要使用ref,ref的值就是对应的bean。如果注入的是基本类型或者string类型就用value,直接将对应的值填入即可。
二、设值注入(setter方式注入)
设值注入就是通过setXxxx方法将bean注入到组件中,自定义类如下
- package cn.glzaction.service.impl;
- import cn.glzaction.service.interfaces.PersonDaoIF;
- import java.util.*;
- public class PersonDaoBean implements PersonDaoIF {
- private String name;
- private Integer id;
- private List list;
- private Map map;
- public void setName(String name) {
- this.name = name;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public void setList(List list) {
- this.list = list;
- }
- public void setMap(Map map) {
- this.map = map;
- }
- @Override
- public void add() {
- // TODO Auto-generated method stub
- System.out.println(map);
- System.out.println(list);
- System.out.println(id);
- System.out.println(name);
- }
- }
采用设置注入只要有setter方法即可,但是有时由于编程习惯也会讲getter方法引进,但是要清楚:设值注入与getter方法无关。这里还有一点需要注意,那就是能使用基本类型,如果非要使用基本类型的话就要使用其对应的包装类型,如上面使用的是Integer而不是int。Spring的xml配置如下:
- <bean id="personDao" class="cn.glzaction.service.impl.PersonDaoBean">
- <property name="name" type="java.lang.String" value="glzaction"/>
- <property name="id" type="java.lang.Integer" value="1"/>
- <property name="list" type="java.util.List">
- <list>
- <value>list1</value>
- <value>list2</value>
- <value>list3</value>
- </list>
- </property>
- <property name="map" type="java.util.Map">
- <map>
- <entry key="key1" value="value1"></entry>
- <entry key="key2" value="value2"></entry>
- </map>
- </property>
- </bean>
设值注入采用的是<property>标签元素,其中的name属性对应的是要注入的变量名,type属性值对应的该变量的类型,可以是自定义类或者包装类型。value属性对应的是相应的值,还有一个ref属性,该属性值对应的是bean。