Spring各对象以一种松耦合的方式,将各层的对象组织在一起,Action不需要关心Service层的具体实现,Service对象无需关心持久层的具体实现,各层对象的调用全面向接口。当系统重构时,代码的改写量将大大减少。
上面所说的一起都得益于Spring的核心机制——依赖注入。通过依赖注入,将Bean与Bean之间以配置文件的形式组织在一起,而不是以硬编码的方式偶合在一起。
Spring的核心是IOC,IOC的抽象概念是依赖关系的转移。全名Inversion of Control,控制反转。字面上可能不好理解,可以先了解DI,Dependency Inversion,依赖反转。简单来说,在进行模块设计时,高层的抽象模块通常是与业务逻辑相关的模块,他应该具有重用性,而不依赖低层的具体实现。
举个例子:
低层模块可能是与硬件相关的软盘存取设计,而高层模块是一个存盘备份的程序需求,如果高层模块直接执行低层模块的方法,就对低层模块产生了依赖关系。如果运行程序的硬件发生了变更,硬件变成了硬盘,那么原来高层直接调用的软盘存取设计就不可用了,这个时候,就需要重新开发硬盘存取的程序并修改高层模块的代码。
public class Business { private FloppyWriter writer = new FloppyWriter(); public void save() { writer.saveToFloppy(); } }
在这个例子中,由于低层模块存取介质的变更,造成了高层模块也必须跟着变更,这不是一个好的设计方式,在设计上希望模块都依赖于模块的抽象,这样才可以重用高层的应用程序设计。
如果以对象导向的方式来设计,Di的解释为程序不应依赖实现,而依赖于抽象接口。
还是上面的例子,通过接口的声明可以改进这种情况。
public interface IDeviceWriter { public void saveToDevice(); }
声明一个用于写入存取介质的接口类。
public class Business { private IDeviceWriter writer; public void setDeviceWriter(IDeviceWriter writer) { this.writer = writer; } public void save() { writer.saveToDevice(); } }
接着设计Business类,遇到存盘需求时,可以设计为依赖于IDeviceWriter接口,而不是依赖于实际的FloppyWriter。
针对软盘存取时,设计一个FloppyWriter类:
public class FloppyWriter implement IDeviceWriter { public void saveToDevice() { // 实际保存到软盘的代码 } }
如果有针对硬盘磁盘的存取设计时,设计一个FixedWriter的类:
public class FixedWriter implement IDeviceWriter { public void saveToDevice() { // 实际保存到硬盘的代码 } }
当程序需要调用保存到硬盘时,可以这样:
Business business = new Business(); business.setDeviceWriter(new FixedWriter()); business.save();
如果程序需要调用软盘时只需要将FixedWriter修改为FloppyWriter即可。
从上面来看,DI的意思:程序不依赖于实现,而程序与实现都依赖于抽象。
IOC为控制反转,A依赖于B,其意义为B拥有对A的控制权,需要转移这种关系,所以将控制权转移到抽象的一方,让抽象方拥有控制权,即:A依赖于抽象C,也就是依赖转移。可以获得组件的可重用性。
依赖注入和控制反转是同一个概念。在传统的程序设计中,通常是由调用者来创建被调用者的实例。但是在Spring中,创建被调用者点工作不在由调用者来完成,因此称为控制反转;创建被调用者的过程由Spring来完成,因此也称为依赖注入。不管是控制反转还是依赖注入,都说明Spring采用动态、灵活的方式来管理各种对象。对象和对象之间的具体实现相互透明。
依赖注入通常有两种:设值注入和构造注入。
-------------------------------------------------------------------------------------------------------
今天先这么多,明天继续设值注入或构造注入。