什么叫IoC

控制反转Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。`

高内聚低耦合可以说是软件技术形态的终极目标。用学术界的话来说,软件的两个本质特性就是构造性和演化性,高内聚低耦合的设计能够让构造和演化都更加高效,比如:

  • 开发更方便组织分工
  • 代码更容易进行复用
  • 更容易进行测试
  • 软件演化有更好的灵活性,能快速响应需求变化,维护代价更小

软件设计各种技术的出现,无一不是朝着这个终极目标的努力。面向对象、基于组件(学术界称为构件)的软件开发、面向切面编程(AOP)、Java近些年流行的模块化方法(比如OSGi技术)等等,这些方法和技术的出现,无外乎都是为了让软件更加高内聚低耦合。与此同时,各路大神还提出各种软件设计原则和模式,来规范我们的软件形态。我们今天谈的IoC也是其中的一个大招。IoC(Inversion of Control​,控制反转)也称为依赖注入(Dependency Injection),作为Spring的一个核心思想,是一种设计对象之间依赖关系的原则及其相关技术。

先来看看字面上怎么来解释:当一个对象创建时,它所依赖的对象由外部传递给它,而非自己去创建所依赖的对象(比如通过new操作)。因此,也可以说在对象如何获取它的依赖对象这件事情上,控制权反转了。这便不难理解控制反转和依赖注入这两个名字的由来了。

一个场景

上面的解释听起来还是有点晦涩,让我们来看看具体的例子吧!

有个土豪老板,我们经常要出差,因此经常要订机票。定机票呢,可以通过去哪儿网订票,也可以通过携程订票。

我们马上可以想到可以通过三个类来表达这个场景,BossQunarBookingServiceCtripBookingService。当然了,我们还应该提供一个BookingService接口,作为QunarBookingServiceCtripBookingService的公共抽象。面向接口编程是面向对象设计的基本原则,如果这都不了解,赶紧先回去看GoF的《设计模式》第一章!

BookingService.java

package com.tianmaying.iocdemo;

public interface BookingService {
void bookFlight();
}

QunarBookingService.java

package com.tianmaying.iocdemo;

public class QunarBookingService implements BookingService {
public void bookFlight() {
System.out.println("book fight by Qunar!"); }
}

CtripBookingService.java

package com.tianmaying.iocdemo;

public class CtripBookingService implements BookingService {
public void bookFlight() {
System.out.println("book fight by Ctrip!");
}
}

好了,土豪出门谈生意,得订机票了,Boss就琢磨着怎么订票呢,Boss比较了一下价格,这一次决定用去哪儿,对应的Boss的代码:

Boss.java

package com.tianmaying.iocdemo;

public class Boss {

    private BookingService bookingService;

    public Boss() {
this.bookingService = new QunarBookingService();
} public BookingService getBookingService() {
return bookingService;
} public void setBookingService(BookingService bookingService) {
this.bookingService = bookingService;
} public void goSomewhere() {
bookingService.bookFlight();
}

在Boss的构造函数中,将其orderService成员变量实例化为​QunarOrderService,goSomewhere()函数中就可以调用orderService的bookFlight方法了!

为了把这个场景Run起来,我们还需要一个main函数:

package com.tianmaying.iocdemo;

public class App {
public static void main(String[] args) {
bossGoSomewhere();
} static void bossGoSomewhere() {
Boss boss = new Boss();
boss.goSomewhere();
}
}

运行之后可以看到控制中可以打印出”book fight by Qunar!”了。

使用IoC的场景

在这个例子中,我们看到Boss需要使用OrderService,于是Boss自己实例化了一个QunarOrderService对象。同志们想想,身为土豪Boss,思考的都是公司战略的事儿,定个票还要自己选择通过什么方式来完成,这个Boss是不是当得实在太苦逼。

所以土豪赶紧给自己找了个美女秘书(别想歪!),Boss要出差时,只需要说一声他需要订票服务,至于是哪个服务,让美女秘书选好后告诉他即可(注入啊!注入!)。(别跟我较真说美女秘书直接把票送上就行!)

这样的话,Boss是不是一身轻松了? 而这个美女秘书还是免费包邮的,这正是Spring扮演的角色!来看看使用Spring之后的代码。

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>

QunarBookingService.java

package com.tianmaying.iocdemo;
import org.springframework.stereotype.Component; @Component
public class QunarBookingService implements BookingService {
public void bookFlight() {
System.out.println("book fight by Qunar!"); }
}

这里我们使用Spring的@Component标注将QunarBookingService注册进Spring的Context,这样它就可以被注入到需要它的地方!相应地,创建QunarBookingService实例的责任也交给了Spring。我们说了,美女秘书帮你搞定嘛!

新建一个SmartBoss类,聪明的老板知道把选择订机票服务这样的杂事交给秘书来做。

SmartBoss.java

package com.tianmaying.iocdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class SmartBoss {
private BookingService bookingService; @Autowired
public void setBookingService(BookingService bookingService) {
this.bookingService = bookingService;
} public BookingService getBookingService() {
return bookingService;
} public void goSomewhere() {
bookingService.bookFlight();
}
}

在上面的代码中,SmartBoss不再自己创建BookingService的实例,只是通过@Autowired标注告诉Spring小秘我需要一个BookingService!

调用代码因此也要做一些小修改,需要创建Spring的Context:

static void smartBossGoSomewhere() {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(
App.class);
try {
SmartBoss boss = context.getBean(SmartBoss.class);
boss.goSomewhere();
} finally {
context.close();
}
}

IoC的好处

回到正题,通过上面的例子,我们来看看IoC到底带来了哪些好处?

Boss没有和某个具体的BookingService类耦合到一起了,这样Boss的维护和演化就更加方便。想象一下,如果Boss需要改用CtripBookingService,这时也不需要修改Boss.java的代码,更换接口的实现非常方便,给Boss注入新的实现即可,轻松惬意。(当然,要做到热插拔还 需要进一步的工作,要么得玩转类加载器这玩意,或者借助OSGi这样的神器)。这也是典型的开放-封闭原则的例子,即对现有模块,功能扩展应该是开放的,而对其代码修改应该是封闭的,即能够做到不需要修改已有代码来扩展新的功能。

想象一下,如果Boss自己直接去实例化QunarBookingService,而QunarBookingService在另外一个Package中甚至另外一个Jar包中,你可得import进来才能使用,紧耦合啊!现在好了,Boss只依赖于抽象接口,测试更方便了吧,Mock一下就轻松搞定!BossQunarBookingService彼此不知道对方,Spring帮两者粘合在一起。

为什么IoC是个大招,因为它会自然而然得促进你应用一些好的设计原则,会帮助你开发出更加“高内聚低耦合”的软件。

IoC的实现

最后我们简单说说IoC是如何实现的。想象一下如果我们自己来实现这个依赖注入的功能,我们怎么来做? 无外乎:

  1. 读取标注或者配置文件,看看Boss依赖的是哪个OrderService,拿到类名
  2. 使用反射的API,基于类名实例化对应的对象实例
  3. 将对象实例,通过构造函数或者setter,传递给Boss

我们发现其实自己来实现也不是很难,Spring实际也就是这么做的。这么看的话其实IoC就是一个工厂模式的升级版!当然要做一个成熟的IoC框架,还是非常多细致的工作要做,Spring不仅提供了一个已经成为业界标准的Java IoC框架,还提供了更多强大的功能,所以大家就别去造轮子啦!希望了解IoC更多实现细节不妨通过学习Spring的源码来加深理解!

[spring入门学习笔记][spring的IoC原理]的更多相关文章

  1. [Spring入门学习笔记][Spring的AOP原理]

    AOP是什么? 面向切面编程 软件工程有一个基本原则叫做“关注点分离”(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题.这年头互联网也 ...

  2. [Spring入门学习笔记][Spring Boot]

    什么是Spring Boot Spring Boot正是在这样的一个背景下被抽象出来的开发框架,它本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速.敏捷地开发新一代基于Spring框架 ...

  3. Spring入门学习笔记(3)——事件处理类

    目录 Spring中的事件处理 Spring内建事件 监听Context事件 Example 自定义Spring事件 Spring中的事件处理 ApplicationContext 是Spring的核 ...

  4. Spring Cloud学习笔记--Spring Boot初次搭建

    1. Spring Boot简介 初次接触Spring的时候,我感觉这是一个很难接触的框架,因为其庞杂的配置文件,我最不喜欢的就是xml文件,这种文件的可读性很不好.所以很久以来我的Spring学习都 ...

  5. Spring Boot学习笔记——Spring Boot与MyBatis的集成(项目示例)

    1.准备数据库环境 # 创建数据库 CREATE DATABASE IF NOT EXISTS zifeiydb DEFAULT CHARSET utf8 COLLATE utf8_general_c ...

  6. [Spring入门学习笔记][静态资源]

    遗留问题 在上一节课的作业中,我们一定遇到了一点问题——虽然将页面内容正确的返回给了浏览器,但是浏览器显示的样式却是不正确的,这是因为在HTML的\标签中我们这样引入了CSS资源: <link ...

  7. [Spring入门学习笔记][创建网站URL]

    设计网站的URL 现代的Web站点都会设计一套拥有明确意义,方便用户记忆的URL,不论是域名还是路径,以天码营为例: http://tianmaying.com/courses表示网站下所有的课程列表 ...

  8. Spring入门学习笔记(2)——基于Java的配置

    目录 基于Java的配置 @Configuration & @Bean Annotations Example 注入Bean依赖 @Import注解 Lifecycle Callbacks(声 ...

  9. Spring入门学习笔记(1)

    目录 Spring好处 依赖注入 面向面编程(AOP) Spring Framework Core Container Web Miscellaneous 编写第一个程序 IoC容器 Spring B ...

随机推荐

  1. openssl 证书请求和自签名命令req详解

    1.密钥.证书请求.证书概要说明 在证书申请签发过程中,客户端涉及到密钥.证书请求.证书这几个概念,初学者可能会搞不清楚三者的关系,网上有的根据后缀名来区分三者,更让人一头雾水.我们以申请证书的流程说 ...

  2. AES算法简介

    AES算法简介 一. AES的结构 1.总体结构 明文分组的长度为128位即16字节,密钥长度可以为16,24或者32字节(128,192,256位).根据密钥的长度,算法被称为AES-128,AES ...

  3. .net 调用Oracle.Data.Access 组件提供的用于批量操作的方法—获取数据库表结构方法和跟参数赋值方法

    1./// <summary> /// 获取当前目标表结构 /// </summary> /// <param name="tableName"> ...

  4. MFC多线程编的可能

    1. 之所以是“可能”,因为这里有个重点就是临时对象是HWND操作的封装,不是窗口类的封装.因此所有的HWND临时对象都是CWnd的实例,即使上面强行转换为CAbcDialog*也依旧是CWnd*,所 ...

  5. slf4j教程

    slf4j只是一个门面(facet),它不包含具体的实现,而是将一些log4j,java.logging等实现包装成统一的接口.借用下图展示了常用日志文件的关系: 通过上面的图,可以简单的理清关系! ...

  6. dedecms likearticle 调用附加表的字段调用方式

    [field:id runphp='yes'] $aid = @me; $row = $GLOBALS['dsql']->GetOne("Select 字段名 From `dede_a ...

  7. dedecms友情链接flink的调用方法

    标记名称:flink[标签简介][功能说明]:用于获取友情链接,其对应后台文件为"includetaglibflink.lib.php".[适用范围]:全局标记,适用V55,V56 ...

  8. linux tar使用

    Linux  tar指令简单使用 -c:创建包,-x:解压或解包(-c和-x可理解为互逆运算),-t:查看包 -f:后加处理文件,必须放在参数组合的最后一位(tar  -cf  a.tar  1.tx ...

  9. 纯js实现div内图片自适应大小

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. secure_file_priv 配置项对数据导入导出的影响

    secure_file_priv  mysqld 用这个配置项来完成对数据导入导出的限制. 例如我们可以通过 select * from tempdb.t into outfile '/home/my ...