Spring的两大核心概念一个是Inversion of Control,一个是Dependency Injection。我们先来梳理一下什么是IoC?IoC的抽象概念是“依赖关系的转移”。例如程序中有两个类A和B。A类中要使用B类中定义的方法,我们通常的做法是在A类中实例化B类一个对象,从而达成调用B类方法的目的。这样做会产生什么问题呢?这样的设计方式,会让A类依赖于B类,也就是说高层模块不应该依赖于低层模块,而模块都必须依赖于抽象,也就是说应该依赖于接口。看一下如下代码:
package ray.spring;
public class Business {
private IDeviceWriter writer;
public IDeviceWriter getWriter() {
return writer;
}
public void setWriter(IDeviceWriter writer) {
this.writer = writer;
}
public void save() {
if (writer == null) {
throw new RuntimeException("Device Writer needed...");
}
writer.saveToDevice();
}
}
通过接口声明,可以达到重复使用Business类的目的。在遇到存档需求时,可以设计为依赖于IDeviceWriter接口,而不是依赖于实际的FloppyWriter。例如:
package ray.spring;
public interface IDeviceWriter {
void saveToDevice();
}
在这样的设计下,Business类就是可以重用的。如果今天有储存至Floppy或USB磁盘的需求,只要针对这两种存储需求分别实现IDeviceWriter接口即可。例如:
package ray.spring;
public class FloppyWriter implements IDeviceWriter {
@Override
public void saveToDevice() {
System.out.println("数据存储在软盘上");
}
}
package ray.spring;
public class UsbDiskWriter implements IDeviceWriter {
@Override
public void saveToDevice() {
System.out.println("数据存储在U盘上");
}
}
编写一个配置管理程序,通过一个简单的XML或是.properties文件来更改配置,就可以简单地更换Business类所依赖的IDeviceWriter实现。例如:
package ray.spring;
import java.io.FileInputStream;
import java.util.Properties;
public class BusinessFactory {
private IDeviceWriter writer;
private Business business;
private Properties properties;
private static BusinessFactory factory;
public BusinessFactory() throws Exception {
properties = new Properties();
properties.load(new FileInputStream("config.properties"));
String businessClass = properties.getProperty("business.class");
String writerClass = properties.getProperty("writer.class");
business = (Business) Class.forName(businessClass).newInstance();
writer = (IDeviceWriter) Class.forName(writerClass).newInstance();
business.setWriter(writer);
}
public IDeviceWriter getWriter() {
return writer;
}
public Business getBusiness() {
return business;
}
public static BusinessFactory getInstance() throws Exception {
if (factory == null) {
return new BusinessFactory();
}
return factory;
}
}
package ray.spring;
public class Main {
public static void main(String[] args) {
try {
Business business = BusinessFactory.getInstance().getBusiness();
business.save();
} catch (Exception e) {
e.printStackTrace();
}
}
}
IoC的Control是控制的意思,其实背后的意义也是一种依赖关系的转移。如果A依赖B,其意义即B拥有控制权。如果转移这种关系(依赖关系的反转即控制关系的反转),将控制权由实现的一方转移至抽象的一方,让抽象方拥有控制权,可以获得组件的可重用性。在上面的Business程序中,整个控制权从实际的FloppyWriter转移至抽象的IDeviceWriter接口上,让Business依赖于IDeviceWriter接口,且FloppyWriter、UsbDiskWriter也依赖于IDeviceWriter。