spring

lbsnm.iteye.com/blog/1275274

在分层的web应用系统中,通常将数据操作分为dao数据层和service业务逻辑层,dao层负责对数据的操作,servlet或action通过service层对数据进行读写,而service层对数据的读写依赖于dao 层对数据的操作,因此存在着类之间的依赖。下面我们通过模拟实验理解不同技术对依赖关系的处理。

例1.在传统的模式中,依赖关系是通过构造对象的方式实现的。

创建如下三个类,模拟从dao到service的功能实现:

class dao_class{

    public String selectUser(String user){

        return "找到了"+user;

    }

}

class service_class{

    dao_class dao=new dao_class();

    public String findUser(String username){

        return dao.selectUser(username);

    }

}

public class dao_service_controller {

    public static void main(String []args)    {

        service_class service=new service_class();

        System.out.println(service.findUser("张三"));

    }

}

运行结果为:找到了张三。

程序中,语句:dao_class dao=new dao_class();使得service_class严重“依赖”dao_class,形成紧密的耦合关系,不符合现代编程理念。

例2.传统的通过接口方式减轻service_class和dao_class耦合程度。

interface dao_interface{

    String selectUser(String user);

}

class dao_class implements dao_interface{

    public String selectUser(String user){

        return "找到了"+user;

    }

}

class service_class implements service_interface{

    dao_interface dao;

    public void setDao(dao_interface dao){

        this.dao=dao;

    }

    public String findUser(String username){

        return dao.selectUser(username);

    }

}

public class dao_service_controller {

    public static void main(String []args)    {

        service_class service=new service_class();

        dao_interface dao=new dao_class();

        service.setDao(dao);

        System.out.println(service.findUser("张三"));

    }

}

通过接口编程,方法:

public void setDao(dao_interface dao){

        this.dao=dao;

    }

使得service_class和dao_class耦合程度大大降低,但是dao_service中的语句:

dao_interface dao=new dao_class();

        service.setDao(dao);

使得dao_service_controller(模拟控制器)和dao_class发生了依赖,因此这种方式也没有解决耦合紧密的问题。

例3. 在例2的基础上通过依赖注入的方式解决:

(1)首先在项目中添加spring库,如图(本例基于NetBeans7.0):

 



 

 

(2)新建“Spring XML配置文件”,命名为“mySpringCfg.xml”:

 



 

 

在步骤3中选择context的Spring名称空间:

 



 

 

(3)在mySpringCfg.xml文件中加入下面语句:

<bean id="daoClass" class="dao_class"/>

    <bean id="serviceClass" class="service_class">

        <property name="dao">

            <ref local="daoClass"/>

         </property>

</bean>

 

表示程序中有两个bean:daoClass和serviceClass,分别代表dao_class 类和service_class类,而“serviceClass”通过“接口”参数“dao”和“daoClass”发生依赖。

(4)修改dao_service _controller类:

public class dao_service _controller{

    public static void main(String []args)    {

           ApplicationContext ctx

=new FileSystemXmlApplicationContext("src/mySpringCfg.xml");

        service_class service=(service_class)ctx.getBean("serviceClass");

        System.out.println(service.findUser("张三"));

    }

}

其中语句:

ApplicationContext ctx=new FileSystemXmlApplicationContext("src/mySpringCfg.xml");

表示引入Spring XML配置文件:“mySpringCfg.xml”创建上下文ctx。

       语句:

service_class service=(service_class)ctx.getBean("serviceClass");

表示通过上下文ctx创建service_class对象,在创建对象时,通过接口“dao”引用dao_class对象,其关联语句分别为:

service_class类中的语句:

dao_interface dao;

    public void setDao(dao_interface dao){

        this.dao=dao;

    }

注意接口成员的名称;

       配置文件mySpringCfg.xml中的语句:

       <property name="dao">

            <ref local="daoClass"/>

     </property>

 

其余的类和接口均不需要变动。可以看到,service_class类只和接口dao_interface成员dao发生关联,和dao_Class类的依赖是通过配置文件发生的,是在创建对象时“注入”的。






Spring 能有效地组织J2EE应用各层的对象。不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调、运行。

Spring将各层的对象以松耦合的方式组织在一起,Action对象无须关心Service对象的具体实现,Service对 象无须关心持久层对象的具体实现,各层对象的调用完全面向接口。当

系统需要重构时,代码的改写量将大大减少。

上面所说的一切都得宜于Spring的核心机制,依赖注入。依赖注入让bean与bean之间以配置文件组织在一起,而不是以硬编码的方式耦合在一起。理解依赖注入

依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,

被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用

者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。

本文研究Spring的三种依赖注入实现类型——接口注入(Interface Injection)、设值注入(Setter Injection)、构造子注入(Constructor Injection)。

Type1 接口注入: 传统的创建接口对象的方法, 借助接口来将调用者与实现者分离。如下面的代码所示:

1 public class ClassA
2 {
3 private InterfaceB clzB;
4 public doSomething()
5 {
6 Ojbect obj = Class.forName(Config.BImplementation).newInstance();
7 clzB = (InterfaceB)obj;
8 clzB.doIt();
9 }
10 ……
11 }


在代码中创建InterfaceB实现类的实例,并将该对象赋予clzB。也就是依据Java中的对象动态多态技术:InterfaceB clzB=new InterfaceBImpleClass();为了将调用者与实现者在

编译期分离,于是有了上面的代码,我们根据预先在配置文件中设定的实现类的类名(Config.BImplementation),动态加载实现类,并通过InterfaceB强制转型后为 ClassA所用。


Type2 设值注入: 在各种类型的依赖注入模式中,设值注入模式在实际开发中得到了最广泛的应用(其中很大一部分得力于Spring框架的
影响)。使用IoC的Setter注射,一些外部元数据被用于解决依赖性问题。并且在Spring中,这种元数据采取了简单的XML配置文件的形式。

下面为某个类的示例代码
(其中包含有一个message属性,该类通过其setMessage()方法获得右容器所提供的值。)

1 public class UpperAction implements Action
2 {
3 private String message;
4 public String getMessage()
5 {
6 return message;
7 }
8 public void setMessage(String string)
9 {
10 message = string;
11 }
12 }
13
其中message 属性的值通过配置文件来提供

1 <bean id="theUpperAction" class="springj2seapp.UpperAction">
2 <property name="message">
3 <value>HeLLo,UpperAction </value>
4 </property>
5 </bean>
6


Type3 构造器注入:即通过构造函数完成依赖关系的设定, 在Type3类型的依赖注入机制中,依赖关系是通过类构造函数建立,容器通过调用类的构造方法,将其所需的依赖关系

注入其中。
public class DIByConstructor {
private final DataSource dataSource;
private final String message;
public DIByConstructor(DataSource ds, String msg) {
this.dataSource = ds;
this.message = msg;
}


示例代码:
配置文件如下
1 <bean id="exampleBean" class="examples.ExampleBean">
2
3 <constructor-arg>
4
5 <ref bean="anotherExampleBean"/>
6 </constructor-arg>
7 <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg>
8 <constructor-arg type="int">
9 <value>1</value>
10 </constructor-arg>
11 </bean>
12 <bean id="anotherExampleBean" class="examples.AnotherBean"/>
13 <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
14

ExampleBean代码:

1 public class ExampleBean
2 {
3 private AnotherBean beanOne;
4 private YetAnotherBean beanTwo;
5 private int i;
6 public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean,
int i)
7 {
8 this.beanOne = anotherBean;
9 this.beanTwo = yetAnotherBean;
10 this.i = i;
11 }
12 }
13
当构造方法中带多个不同的基本数据类型的参数时,为了避免产生二义性,可以采用type或者index来指定构造方法的参数的类型和顺序。
如:
type方法
1 <constructor-arg type="int">
2 <value>7500000</value>
3 </constructor-arg>
4 <constructor-arg type="java.lang.String">
5 <value>42</value>
6 </constructor-arg>
7
index方法
1 <bean id="exampleBean" class="examples.ExampleBean">
2 <constructor-arg index="0">
3 <value>7500000</value>
4 </constructor-arg>
5 <constructor-arg index="1">
6 <value>42</value>
7 </constructor-arg>
8 </bean>
9
总结:
type1在灵活性、易用性上不如其他两种注入模式, Type2 和Type3型的依赖注入实现则是目前主流的IOC实现模式, Type3 和Type2模式各有千秋,而Spring都对Type3和

Type2类型的依赖注入机制提供了良好支持。 以Type3类型为主,辅之以Type2类型机制作为补充,可以达到最好的依赖注入效果,不过对于基于Spring Framework开发的应用而言

,Type2使用更加广泛。


Type3 构造器注入的优势:
1. “在构造期即创建一个完整、合法的对象”,对于这条Java设计原则,Type2无疑是最好的
响应者。
2. 避免了繁琐的setter方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,
更加易读。
3. 由于没有setter方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处于
相对“不变”的稳定状态,无需担心上层代码在调用过程中执行setter方法对组件依赖关系
产生破坏,特别是对于Singleton模式的组件而言,这可能对整个系统产生重大的影响。
4. 同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。
对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的
层次清晰性提供了保证。
5. 通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量
依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要,比如某个依赖关系注入的
先决条件是组件的DataSource及相关资源已经被设定。
Type2 设值注入的优势
1. 对于习惯了传统JavaBean开发的程序员而言,通过setter方法设定依赖关系显得更加直
观,更加自然。
2. 如果依赖关系(或继承关系)较为复杂,那么Type2模式的构造函数也会相当庞大(我们需
要在构造函数中设定所有依赖关系),此时Type3模式往往更为简洁。
3. 对于某些第三方类库而言,可能要求我们的组件必须提供一个默认的构造函数(如Struts
中的Action),此时Type2类型的依赖注入机制就体现出其局限性,难以完成我们期望的功
能。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值