1.前言
第一次接触spring框架时,概念里它是一个很人性化的Web容器,通常只需要配置bean文件以及数据源,一个demo就能很顺利的跑起来,确实节省了很多代码,可以把更多的精力放在业务逻辑上,但是并不知道其博大精深之处,但凡看到什么设计模式、依赖倒置、控制反转、面向切面,就觉得很费解,很多书一上来就把控制反转的文字概念搬上来,甚至没有举例,让人无所适从。直到前段时间看了一些源码,重拾设计模式,才有点恍然大悟,原来没有那么难理解,当初只是被一些高深的文字唬住了,说这么多不就是为了解耦嘛。
2.什么是IOC
IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
3.什么是DI
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的,DI是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。## 附录
4.IOC和DI的关系
按照我的理解,其实它们是同一个概念的不同角度描述,控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),而DI“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。所以在讨论时我更喜欢用DI而不是IOC来介绍spring
5.什么是容器
通过前面的描述,我们知道了IOC和DI的作用就是为了解决程序耦合,具体怎么解决请看文章的附录,其具体过程大概就是:
我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件,创建和获取三层对象的类就是工厂。
那么这些读取到的对象应该存放在什么地方?由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。
到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。所以我们的答案就是在应用加载时,创建一个 Map,用于存放三层对象。我们把这个 map 称之为容器,spring IOC/DI的更为直观的叫法是容器,这是因为spring可以管理很多类,当需要某一类对象的实例时,spring就会提供相应的实例,就像是一个容器里面,所以我们经常说IOC容器
6.附录
程序耦合:
现在有Knight
和Quest
骑士和探险两个接口,RescueQuest实现了Quest接口,它只能进行拯救任务
public class BraveKnight implements Knight{
private RescueQuest quest;
public BraveKnight (){
this.quest = new RescueQuest ();
}
public void actionOnQuest(){
quest.action();
}
}
可以看到,BraveKnight
在它的构造函数中自行创建了RescueQuest
对象,这使得这两个对象高度耦合,而且这个骑士类的功能也受到限制,只能执行拯救任务,如果有其他探险比如杀掉一条龙这里不能完成,更糟糕的是在单元测试中必须当保证骑士的actionOnQuest()
被调用的时候,探险类的action()
也要被执行,这将导致代码难以测试,难以复用,一个地方出现Bug,将会导致更多地方出现bug
如何解耦
通过工厂模式解耦:
在传统的获取对象的方式是通过new来获取对象,是主动的获取
现在我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一,那么什么是控制反转呢:
控制反转(Inversion of Control简写为IoC)
概念:
- 控制反转(Inversion of Control简写为IoC,把对象的创建交给框架,它包括依赖注入和依赖查找,它的作用是降低程序间的耦合(依赖关系)
依赖注入:
**依赖注入(Dependency Injection,简称DI)**它的作用就是让对象之间保持松散耦合,具体实现由spring容器提供,在传统开发中:一个类中包含其他类的对象,比如BraveKnight
类中的RescueQuest
对象,骑士依赖探险,它们之间的关系被称为依赖关系,BraveKnight
负责管理与自己相互协作的对象的引用RescueQuest quest
,这会导致程序的高度耦合
我们知道了可以通过工厂模式机型解耦,在Spring中,对象的依赖关系通过依赖注入(DI)由系统中负责协调各对象的第三方组件在创建对象的时候进行设定,这里的第三方组件就是我们所说的工厂,在spring中的体现为Spring容器,
如图所示,依赖注入会将所依赖的关系自动交给目标对象,而不是让对象自己去获取依赖,依赖关系将被自动注入到他们的对象中去
下面我们通过一个案例来了解一下具体的执行过程:
/**
*探险类接口
*/
public interface Quest {
public void action();
}
/*
*骑士接口
*/
public interface Knight { }
/**
* 骑士类
*/
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
public void actionOnQuest(){
quest.action();
}
}
/**
*探险类
*/
public class RescueQuest implements Quest {
public void action() {
System.out.println("执行拯救任务");
}
}
我们改动了BraveKnight的结构,将quest的实现通过构造器的方式实现注入,只要实现了Quest接口都可以传进来,这就保证了骑士可以完成不同的探险任务
除了XML进行配置还可以基于java的配置,在这里不予演示
通过上述代码我们就可以回答之前的问题:为什么会简化开发,
spring还有其他简化开发的方式,这里只介绍了IoC依赖注入来展示
Spring的体系结构:
让我们分别浏览这些模块
- Spring核心容器
容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建,配置和管理,在该模块中包括了Spring bean工厂,它为Spring提供了DI功能,基于bean工厂我们还会发现有多种Spring上下文实现的方式,每一种都提供了配置Spring的不同方式 - SpringAOP模块
面向切面编程 - 数据访问与集成
有不同的方式实现,可以是Spring Data JPA或Mybatis或其他ORM,也可以是Spring中的JDBC模板 - Web与远程调用
MVC模式是一种被普遍接受的构建web应用的方法,如当前流行的springmvc框架
当我们具体的开发一个企业级Java应用时,这些模块都会提供我们所需要的一切,我们只需要在pom文件中导入需要的坐标即可,我们可以根据需求自主搭配相应的模块而不是将整个应用完全建立在Spring框架之上,比如数据访问与集成,可以用Spring来整合其他的框架比如Mybatis而不是Spring提供的Spring Data了