适合新手看的Spring IOC来龙去脉
来实习之前也做了点准备,去了解了一下Spring,字面上了解了Spring的IOC(控制反转)和DI(依赖注入)。但现在回头一看,发现其实若一名编程新手刚刚接触Java,那么他或她应该是很大概率上不能很快地理解这些概念和应用场景的。我是直到参加实习快一个月,自己去探索了一些资料后,才慢慢有了一些概念。这篇文章并没有源码解读之类的,只是提供了一条渐进的知识路线,让像我一样的新手了解Spring IOC的来龙去脉。
啥是框架?
我在打过一些代码之后,慢慢也感受到设计模式对Java开发的重要性,所以就零零碎碎地看了一些相关的博客或书籍章节(推荐《研磨设计模式》)。这些资料里就提到什么是框架。
框架我之前也接触过,比如安卓、Django、Scrapy。当我自己在理解什么是框架的时候,我把它理解为写文章之前的一个大提纲,写文章之前列提纲的好处就是,使得我们接下来写文章变成一个个简便的段落填空工作。我对程序的框架最开始的理解也是这样,就是有了框架作为程序的“大纲”之后,接下来我们的开发工作就变成一个个简便的程序填空工作。后来我看到《研磨设计模式》里的工厂方法模式的介绍,里面提到说:
框架就是能完成一定功能的半成品软件。
我一想,这样解释的确是更准确的,框架的目的就是帮助开发者完成一些基础的、有难度的、通用的功能,避免开发者在开发应用时要完全重头开始。那意思就是说框架里完成的这些功能,就是开发者等一下在应用开发的时候要用到的。那等一下应用开发者要怎么用框架里的功能?
像我这种新手,就觉得,框架里把所有可用的功能什么的写成一个个类,等一下应用开发者要用,就直接调用就行了。这种想法就是以前在自己的小世界里胡乱搞、做小东西的时候的想法,现在回头看看,就图样图森破了。
假如是按照这样的想法来设计框架,等一下这个框架就会变得庞杂而且不灵活,因为这样的框架会是一种所谓的侵入式框架,也有叫做重量级框架的。在使用这种框架时,要继承这个框架提供的实体类或实现框架提供的接口,导致程序本身对框架有了很强的依赖,当去除框架时,程序也就无法运行了。而Spring的IOC和DI,就是提供了相反的非侵入式框架、也就是轻量级框架的解决方案。
那Spring的IOC和DI是怎么解决这个问题的呢?要了解,就得先知道简单工厂。
啥是简单工厂?
首先得介绍Java中接口的思想。接口通常被用来作为实现类的外观,也就是实现类的行为定义。浓缩下来,接口的思想就是“封装隔离”。生活中处处是例子,比如一块手表,我们只需要看到表盘上几根针在走就行了,我们不用去理解手表里面的其他构造。类也是这样,有很多时候开发者只需要知道类所实现的接口,就能满足开发要求了。这就有了面向接口编程。
我们若不用简单工厂,怎么使用接口?
假如有个接口叫Api:
/** * 一个叫Api的接口 */ public interface Api { public void test1(String s); }
一个类实现了它:
/** * 一个实现了Api接口的类 */ public class Impl implements Api { @Override public void test1(String s) { System.out.println("s is"+s); } }
客户端里的用法:
/** * 客户端中,不能用接口创建对象,只能用实现类 */ public class Client { public static void main(String[] args) { Api api = new Impl(); api.test1("nihao,shijie"); } }
这样一来就有问题了,我们刚刚提到说“很多时候开发者只需要知道类所实现的接口”,但是刚刚的例子里面,开发者不仅需要知道接口,还要知道接口的实现类,这就是问题所在了。我看手表的时候,难道还得去了解清楚手表里面齿轮是怎么转动的,才能知道时间?这明显不合理。这里简单工厂的出现就是为了解决这个问题。
如上图,我们提供一个工厂类,这个工厂类的内部可以知道接口的实现,而客户端只需要知道这个工厂类,实现的事情交给工厂去办就行了。这个过程中,把了解实现类的职责交给工厂统一去办,无论接口有多少实现,统统让工厂去了解。我这边客户端只需向工厂吆喝一句“我要啥啥手表”,工厂接收到,就去实现出来我要的手表,返回给我。
比如刚刚的Api有两个实现类:
/** * 一个实现了Api接口的类ImplA */ public class ImplA implements Api { @Override public void test1(String s) { System.out.println("This is ImplA, "+s); } }
/** * 一个实现了Api接口的类ImplB */ public class ImplB implements Api { @Override public void test1(String s) { System.out.println("This is ImplB, "+s); } }
用工厂类来帮忙创建对象:
/** * 创建Api对象的工厂类 */ public class Factory { public static Api createApi(int condition) { Api api = null; //工厂根据条件来创建某一个具体实现类 if (condition == 0) { api = new ImplA(); } else if (condition == 1) { api = new ImplB(); } return api; } }
客户端可以如愿用工厂来创建对象:
/** * 客户端中,试用工厂创建对象 */ public class Client { public static void main(String[] args) { Api api = Factory.createApi(1); api.test1("nihao,shijie"); } }
可以看到简单工厂模式的所做的改变,就是把 new Impl() 这项工作,移到工厂内,的确是比较简单。但是从模式上,就实现了接口编程的“封装隔离”,有重要意义。
如果理解了上面所说的,就可以理解到简单工厂的
好处:封装、解耦;
坏处:可能复杂化客户端(客户端必须了解工厂,比如上面通过传入条件参数来获取指定对象)、不方便拓展。
真对于这两个坏处,Spring又出场了,Spring提出的IOC和DI,就提供了一种比较好的解决方案。Spring中的工厂,通过读取配置文件,利用Java的反射机制,找到借口的实现类,从而帮助创建实现类。
在Spring中我们写好配置文件:
<bean id="api" class="com.lhz.learningIOC.Impl" />
客户端里我们可以这样用:
Api api = (Api) context.getBean(“api”);
里面原理也没有非常复杂,就是Spring有个类去读取了这个配置文件,运用Java反射机制(根据给出的类名来生成对象)把这个对象实现出来了,返回给客户端。这就是Spring IOC,也就是DI。让Spring作为一个简单工厂,帮助客户端实现类。
Spring IOC的意义
其实目前来讲我所理解到的,Spring中的DI,就是IOC的一种实现方式。所以我以后就讲IOC就好了。
简单工厂的意义,就是把创建对象的职责交给第三方:简单工厂;
而Spring IOC的意义,就是把创建对象的职责交给第三方:Spring容器。
但凡稍微是个系统,都是两个或者更多的类组成,假如每个对象负责管理与自己合作的素有对象的引用,就会导致产生高度耦合和难以测试的代码,Spring容器作为第三方,承担了这个职责,负责创建对象、管理对象生命周期等等,从而让系统实现松耦合,让框架实现轻量、非侵入。这就是Spring IOC的意义。

浙公网安备 33010602011771号