开始学习Spring的时候,对依赖注入(DI)——也叫控制反转(IOC)—— 的理解不是很深刻。随着学习的深入,也逐渐有了自己的认识,在此记录,也希望能帮助其他入门同学更深入地理解Spring。本文不再介绍其背景与定义,比 如“究竟是什么控制被反转了?”、“注入了什么依赖?”等等问题,在网络上应该会搜出很多相关的内容。本文下面主要从入门者的角度来分析如下问题:“依赖 注入要解决什么问题?” 以及 “依赖注入可能是如何实现的?”

==============================

为了更好地解释这两个问题,本文列举了一个简单的代码小例子来配合说明,先将例子中设计的类列举如下:

比如我有苹果(Apple)、橘子(Orange)、香蕉(Banana)三种水果(Fruit),我想知道某种水果是什么颜色的。

对应水果类的实现代码非常简单:

Fruit是个接口:

package com.example.impl;

public interface Fruit {
//只有一个简单的方法:返回水果的颜色
public String getColor();
}

Apple、Banana、Orange三个类各自实现Fruit接口:

package com.example.action;
import com.example.impl.Fruit; public class Apple implements Fruit {
public String getColor() {
return "Red";
}
} --------------------------------------------
package com.example.action;
import com.example.impl.Fruit; public class Banana implements Fruit {
public String getColor() {
return "Yellow";
}
} --------------------------------------------
package com.example.action;
import com.example.impl.Fruit; public class Orange implements Fruit {
public String getColor() {
return "Orange";
}
}

==============================

OK,现在开始解释文章开头提出的两个问题:

(1)依赖注入要解决什么问题?

简单地说,依赖注入就是为了“对类之间的依赖进行解耦”。

比如上面的水果例子中,如果现在我想知道某种水果的颜色,我需要这样实现:

package com.example.test;
import com.example.impl.Fruit;
import com.example.action.*; public class Person {
public static void main(String[] args) {
//我想知道苹果的颜色.
Fruit fruit = new Apple();
System.out.println(fruit.getColor());
}
}

这里,我(Person)与水果类(这里具体为Apple)之间是有依赖的,即Person依赖Fruit,如果我现在想知道香蕉的颜色,那我只能修改Person类的代码如下:

package com.example.test;
import com.example.impl.Fruit;
import com.example.action.*; public class Person {
public static void main(String[] args) {
//我现在改成想知道香蕉的颜色了...
Fruit fruit = new Banana();
System.out.println(fruit.getColor());
}
}

这就是依赖产生的问题:我每次都必须修改Person的代码,才能实现“知道不同Fruit的颜色”的目的。

依赖注入要解决的就是上面这个问题,套在本例中就是:如果我想知道不同Fruit的颜色,我不需要修改Person的代码。也就是Person与Fruit解耦啦!!

那依赖注入具体是怎么实现的呢?且继续往下看。

(2)依赖注入可能是如何实现的?

之所以加“可能”二字,是因为我还没拜读过Spring的内部代码,所以暂时是靠自己的理解认为是这么实现的,但我估计应该八九不离十吧。

上面例子中说到,Person现在是对Fruit有依赖,Spring第一个想到的就是引入Java的反射机制,关于反射本文不再解释,直接拿过来用了,引入反射之后,Person的代码会变成这样:

package com.example.test;
import com.example.impl.Fruit;
import com.example.action.*; public class Person {
public static void main(String[] args) {
try {
//使用Java反射机制,使Person对Fruit具体类的依赖,转变为对类名的字符串的依赖...
Fruit fruit = (Fruit) Class.forName("com.example.action.Apple").newInstance(); //注意:forName()需要传入类的全路径名称
System.out.println(fruit.getColor());
}
catch (Exception e) {
System.out.println("class not found.");
}
}
}

代码看起来复杂了很多,但实际上只有标红的一行是最主要的变化,其余都是异常处理的代码。

可以看到,这个时候,Person对Fruit的依赖,已经转变成了对Fruit下面某个具体类的类名字符串的依赖了,即上面代码中,那段蓝色的字符串。如果此时我想知道橘子的颜色,我只需要把上面蓝色字符串的内容改成:com.example.action.Orange即可。

但是,现在Person还是没有完全摆脱对Fruit的依赖,只是依赖变成了一个字符串而已,那Spring第二个想到的就是通过文件读取这个类名字符串了,我们先在D盘建立一个test.txt文件,然后把“com.example.action.Apple”这段内容写到这个test.txt文件中,同时代码改为:

package com.example.test;
import com.example.impl.Fruit;
import com.example.action.*; public class Person {
public static void main(String[] args) {
try {
FileReader fr = new FileReader("D://test.txt");
BufferedReader br = new BufferedReader(fr);
String fruitName = br.readLine();
//使用Java反射机制,使Person对Fruit具体类的依赖,转变为对类名的字符串的依赖...
//加上读取文件的配置,类名字符串是从文件中读取的,不直接写在Person中
Fruit fruit = (Fruit) Class.forName(fruitName).newInstance(); //注意:forName()需要传入类的全路径名称
System.out.println(fruit.getColor()); br.close();
fr.close();
}
catch (Exception e) {
System.out.println("class not found.");
}
}
}

上面代码其实就是从D://test.txt中读取第一行内容,即类名字符串,然后利用java反射机制实例化这个类。

至此,可以看到Person类也不需要依赖Fruit的类名字符串了,转而变成了对D://test.txt这个文件的依赖,但是我们不需要修改这个文件的地址和文件名,只需要修改文件内容,就可以动态地知道不同水果的颜色了。比如我把test.txt的内容改成com.example.action.Banana,运行程序后,自然会打印出Yellow而不再是Red了。

而Spring实际上是把上文的txt文件替换成了xml文件,这样做的好处?xml文件中的标签更容易对类的各种属性进行表述,比如类名、类属性、类属性的值等等,不用像txt中那种没层次关系、需要自己瞎定义了。自然是更方便使用。

Spring实际使用时,大概就是把上面读取文件+反射的过程封装成ApplicationConext类,所以不需要在Person中写这么大段的代码了,而是直接用ApplicationContext和getBean()即可,或者直接用注解的方式更是方便,其实可以理解为Spring是把这些代码都封装到了框架里,不需要我们自己写了。

以上就是我在学习Spring依赖注入的过程中,一些自己的心得体会,希望大家多多交流、多提意见和建议!

浅析“依赖注入(DI)/控制反转(IOC)”的实现思路的更多相关文章

  1. 依赖注入 DI 控制反转 IOC MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. ADO.NET .net core2.0添加json文件并转化成类注入控制器使用 简单了解 iTextSharp实现HTML to PDF ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下 C# AutoMapper 了解一下

    ADO.NET   一.ADO.NET概要 ADO.NET是.NET框架中的重要组件,主要用于完成C#应用程序访问数据库 二.ADO.NET的组成 ①System.Data  → DataTable, ...

  3. 依赖注入 DI 控制反转 IOC 概念 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下

    先简单了解一这个几个 名词的意思. 控制反转(IOC) 依赖注入(DI) 并不是某种技术. 而是一种思想.一种面向对象编程法则 什么是控制反转(IOC)?  什么是依赖注入(DI) 可以点击下面链接 ...

  5. ASP.NET中IOC容器Autofac(依赖注入DI 控制反转IOC)

    IOC的一个重点是在程序运行中,动态的向某个对象提供它所需要的其他对象.这一点是通过DI来实现的.Autofac则是比较流行的一款IOC容器. IoC和DI有什么关系呢?其实它们是同一个概念的不同角度 ...

  6. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  7. 浅谈(IOC)依赖注入与控制反转(DI)

    前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ...

  8. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->Spring Framework的依赖注入和控制反转

    Dependency Injection and Inversion of Control 1.概述: 1.1相关概念 bean:由IoC容器所管理的对象,也即各个类实例化所得对象都叫做bean 控制 ...

  9. 简单解析依赖注入(控制反转)在Spring中的应用

    IoC——Inversion of Control  控制反转DI——Dependency Injection   依赖注入 大家都知道,依赖注入是Spring中非常重要的一种设计模式.可能很多初学者 ...

随机推荐

  1. Win10 Build9926 更新问题解决

    将Dns 改为 4.2.2.2 备用 4.2.2.1

  2. Mac Pro 16G 安装MyEclipse提示虚拟内存(为0)不够

    百度一下很多人都说开多一点程序,让程序占满内存,使其虚拟内存使用就能通过这一步骤,但这里有个更好一点的方案 通过执行: memory_pressure -l critical 用系统内存压力测试进程占 ...

  3. Android开发之---Activity启动模式

    在Android开发中,启动一个新的activity我们可以使用startActivity或startActivityForResult,Android系统使用栈的方式来管理一个APP的页面显示与保存 ...

  4. c语言中遇到“警告: the `gets' function is dangerous and should not be used.”的解决办法

    写于2016年12月1日. 在用c的库函数gets(str)时,编译出现该提示.原因在于linux下gcc不支持gets命令,要换成fgets(arr,size,stdin).

  5. Class.forName()的作用

    大家都用过Class.forName(),也都知道是类加载的作用,其实这方法不只是类加载,还有类初始化. 下面用个小例子说明一下: A类,是用来加载的类 /** * 用来测试类加载的类此类有 * 静态 ...

  6. [C++][数据结构][算法]单链式结构的深拷贝

    深拷贝(deep-copy),区别于浅拷贝,表示复制所有数据,而不是像浅拷贝一般只复制指针.深拷贝的数据不会因原始数据被delete后而消失. 单链式结构可以实现单链表,栈,队列,树等数据结构.掌握了 ...

  7. Redis学习笔记(4) Redis事务、生存时间及排序

    1. Redis事务 Redis中的事务(transaction)是一组命令的集合,一个事务中的命令要么都执行,要么都不执行.事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次 ...

  8. Android UI 之实现多级列表TreeView

    所谓TreeView就是在Windows中常见的多级列表树,在Android中系统只默认提供了ListView和ExpandableListView两种列表,最多只支持到二级列表的实现,所以如果想要实 ...

  9. AngularJS学习之依赖注入

    1.什么是依赖注入:简称DI,是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分. 该模式分离了客户端 ...

  10. Angular JS 学习之Bootstrap

    1.要使用Bootstrap框架,必须在<head>中加入链接: <link rel="stylesheet" href="//maxcdn.boots ...