Spring到底是什么,包括哪些内容(二)
先回答上一篇博文中的主要问题:为什么Spring的控制反转非常受欢迎并且广泛被使用。
先从一个需求开始:
一家公司最初使用百度云服务,后来更换为阿里云服务,再后来更换为其它公司的云服务…
⒈、正常的Java代码执行模式:
⑴、公司:
public class Company {
...
//使用百度云服务,持有它的引用
private BaiduServerService Service;
//构造方法
public Company(BaiduServerService Service){
//初始化云服务
this.Service = Service;
}
//使用云服务
public void Service() {
Service.Service();
}
...
}
⑵、创建百度云服务:
public class BaiduServerService {
//百度云提供服务
public void Service() {
System.out.print("百度云服务");
}
...
}
⑶、测试:
import com.Company.www.Company;
import com.ServerService.www.BaiduServerService;
public class Test {
public static void main(String[] args) {
//创建百度云服务对象
BaiduServerService service = new BaiduServerService();
//创建公司对象,传入百度云服务对象
Company company = new Company(service);
//使用百度云服务
company.Service();
}
}
结果示例:
⑷、更换为阿里云服务:
①、首先将公司持有的云服务引用由百度云更换为阿里云:
public class Company {
...
//公司持有的云服务引用类型由BaiduServerService
//(百度云服务)更换为AliBabaServerService
//(阿里云服务)
private AliBabaServerService Service;
//构造方法
public Company(AliBabaServerService Service){
//初始化云服务
this.Service = Service;
}
//使用云服务
public void Service() {
Service.Service();
}
...
}
②、创建阿里云服务:
public class AliBabaServerService {
//阿里云提供服务
public void Service() {
System.out.print("阿里云服务");
}
...
}
③、重新测试:
import com.Company.www.Company;
import com.ServerService.www.AliBabaServerService;
public class Test {
public static void main(String[] args) {
//创建阿里云服务对象
AliBabaServerService service = new AliBabaServerService();
//创建公司对象,传入阿里云服务对象
Company company = new Company(service);
//使用阿里云服务
company.Service();
}
}
结果示例:
⑸、更换为华为云服务:
…
⑹、换回百度云服务:
…
如果有兴趣,可以自行尝试实现后面更换云服务的代码,在这里就不一一列出了。
总结一下上面代码模式存在的问题:
①、代码维护(更换云服务)非常繁琐,更何况这里只是简单的Java示例类,实际的应用类代码维护可想而知。
②、重复性代码比重过大(对比百度云服务和阿里云服务的代码,重复性比例至少百分之八十)。
③、无法测试(一种云服务类型就需要匹配一个测试类)。
代码组织架构截图:
上面的代码能不能通过某种方式改进,降低维护的成本?
有,通过接口降低类之间的耦合度。
⑴、新增云服务接口:
interface ServerService{
public void Service();
}
⑵、公司与云服务接口耦合,不再与具体的云服务耦合:
import com.ServerService.www.ServerService;
public class Company {
//持有云服务接口引用
private ServerService Service;
//构造方法
public Company(ServerService Service){
//初始化云服务
this.Service = Service;
}
//使用云服务
public void Service() {
Service.Service();
}
}
⑶、创建百度云服务:
//implements云服务接口ServerService
public class BaiduServerService implements ServerService{
public void Service() {
System.out.print("百度云服务");
}
}
⑷、测试:
import com.Company.www.Company;
import com.ServerService.www.BaiduServerService;
import com.ServerService.www.ServerService;
public class Test {
public static void main(String[] args) {
//接口引用持有子类对象(多态)
ServerService service = new BaiduServerService();
//创建公司对象,传入云服务对象(实际是百度云对象)
Company company = new Company(service);
//使用云服务(实际是百度云服务)
company.Service();
}
}
⑸、更换为阿里云服务:
①、创建阿里云服务(需要implements云服务接口ServerService):
//implements云服务接口ServerService
public class AliBabaServerService implements ServerService{
//阿里云提供服务
public void Service() {
System.out.print("阿里云服务");
}
...
}
②、重新测试:
```javascript
import com.Company.www.Company;
import com.ServerService.www.AliBabaServerService;
public class Test {
public static void main(String[] args) {
//由BaiduServerService(百度云服务)更换为
//AliBabaServerService(阿里云服务)
ServerService service = new AliBabaServerService();
//创建公司对象,传入云服务对象(实际是百度云对象)
Company company = new Company(service);
//使用阿里云服务
company.Service();
}
}
总结一下上面代码模式存在的问题:
①、代码维护(更换云服务)繁琐程度稍有降低,至少在更换云服务的过程中不再需要维护公司类。
②、重复性代码比重稍有降低。
③、无法测试的状况仍然没有改变(如图)。
因为更换云服务始终需要修改测试类的内容才能测试。
还有没有什么方式能进一步降低维护的成本?
有,通过Spring的控制反转。
⑴、云服务接口:
interface ServerService{
public void Service();
}
⑵、公司(与云服务接口耦合,不再与具体的云服务类耦合):
import com.ServerService.www.ServerService;
public class Company {
//持有云服务接口引用
private ServerService Service;
//构造方法
public Company(ServerService Service){
//初始化云服务
this.Service = Service;
}
//使用云服务
public void Service() {
Service.Service();
}
}
⑶、创建百度云服务:
//implements云服务接口ServerService
public class BaiduServerService implements ServerService{
public void Service() {
System.out.print("百度云服务");
}
}
⑷、将所有的类都配置到Spring的配置文件中:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
//通过Spring配置文件的<Bean>标签配置类Company
<bean id="company" class="com.Company.www.Company">
//具体的标签意义后续的博文会详细描述,这里简单说明
//这个标签的存在意义是说明类Company与类BaiduServerService
//的关系
<constructor-arg ref="Baidu" />
</bean>
//通过Spring配置文件的<Bean>标签配置类BaiduServerService
<bean id="Baidu" class="com.ServerService.www.BaiduServerService">
</bean>
</beans>
⑸、测试:
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import com.Company.www.Company;
@SuppressWarnings("deprecation")
public class Test {
public static void main(String[] args) {
//注解,表示已经废弃,不再使用
@SuppressWarnings("deprecation")
//创建一个容器对象(容器的来源是XML文件"Bean.xml")
XmlBeanFactory factory = new XmlBeanFactory
(new ClassPathResource("Bean.xml"));
//从容器中获取一个名为"company"的对象
Company company = (Company) factory.getBean("company");
//使用云服务
company.Service();
}
}
结果示例:
⑹、更换为阿里云服务:
①、创建阿里云服务(需要implements云服务接口ServerService):
```javascript
public class AliBabaServerService {
//阿里云提供服务
public void Service() {
System.out.print("阿里云服务");
}
...
}
②、将阿里云服务类配置到Spring的配置文件中:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
//通过Spring配置文件的<Bean>标签配置类Company
<bean id="company" class="com.Company.www.Company">
//更换云服务的重点在这里:只需要更换
//Company与具体云服务类的关系
<constructor-arg ref="AliBaba" />
</bean>
//可以将百度云服务类保留或者注释,不建议删除
<!--<bean id="Baidu" class="com.ServerService.www.BaiduServerService">
</bean> -->
//通过Spring配置文件的<Bean>标签配置类AliBabaServerService
<bean id="AliBaba" class="com.ServerService.www.AliBabaServerService">
</bean>
</beans>
③、测试:
同上,因为测试类不需要作任何修改。
结果示例:
总结一下上面代码模式存在的问题:
①、代码维护(更换云服务)繁琐程度大大降低。
②、重复性代码比重大大降低。
③、测试非常方便。
目前的编程环境都提倡低侵入式,就拿更换云服务的例子来说,你只能关注业务本身,也就是提供新的云服务类型以及实现,其它内容(比如测试代码)根本无法修改。
Spring的控制反转非常适合目前的编程环境,如果不满意当前的实现,那么就提供新的实现,再将新的实现丢进Spring的容器中,剩下的都交给容器(相对而言,Spring配置文件替换维护部分可以忽略不计)。
Spring的控制反转提供的另一个附加好处是:所有的维护都集中在一个文件中,非常方便。
PS:时间有限,有关Spring的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正。