一、概述

  • Spring是一个轻量级的开源JavaEE框架

  • Spring可以解决企业应用开发的复杂性

  • Spring两大核心部分:IoC和AOP

  • 特点:

    • 方便解耦,简化开发

    • AOP编程支持

    • 方便程序测试

    • 方便和其他框架整合

    • 方便事务操作

    • 降低API开发难度

IoC和AOP是Spring的核心,咱们就从这两个俩分析其原理,入门案例这里就不写了,直接进入主题,先来讲述IoC,下篇文章将讲述AOP

二、IoC解耦推导

我们都知道,IoC是控制反转,通俗讲就是把对象创建和对象之间的调用交给Spring管理,通过简单的xml配置就可以创建和调用对象,其主要目的就是解耦,降低代码之间的耦合度,咱们就从传统方式到IoC来一步一步讲述怎样把耦合度降到最低。

所谓解耦就是降低类之间的依赖和方法之间的依赖

1. 传统直接调用对象

在我们传统的开发方式中,是直接采取new对象的方式创建对象,经常可以看到service调用dao这样的代码,如果是直接调用,我们来看看是怎么样子的,创建UserService和UserDao ,通过UserService调用UserDao:

// UserDao1类public class UserDao1 {    public void say(){        System.out.println("I am userDao1");    }}// UserService类,调用UserDao1类中的方法public class UserService {    public void getUser(UserDao1 userDao1){        userDao1.say();    }}

在上面的代码中,是最传统的调用方式,通过service调用dao,可以得到我们想要的结果,打印出:“I am userDao1”,但是,突然,产品经理想要UserDao2一个新的类也可以在UserService中进行调用,这个时候,就需要将代码改为如下:

// UserDao1类public class UserDao1 {    public void say(){        System.out.println("I am userDao1");    }}// UserDao2类public class UserDao2 {    public void say(){        System.out.println("I am userDao2");    }}// UserService类,调用UserDao1和UserDao2类中的方法public class UserService {    public void getUser(UserDao1 userDao1){        userDao1.say();    }        public void getUser(UserDao2 userDao2){        userDao2.say();    }}

可以看到,我们不仅要新建一个UserDao2类,还需要修改UserService中的代码,万一,突然,产品经理想把UserDao3、UserDao4、UserDao5.....这样具有相同功能的类也在userService中作为参数进行调用,新建这些类倒还好,避免不了,问题是还要修改UserService类,简直头大....

其实,上面的代码中,UserService就和要调用的dao类具有一种很强的联系,我们把这种联系称为强耦合关系,这种强耦合关系是不利于开发的,因此我们需要解耦,首先想到的便是使用接口进行解耦,也就是面向接口编程。

2. 接口解耦

将上面的代码进行修改,将UserDao定义为接口,然后去实现这个接口,再进行调用,如下:

// UserDao接口public interface UserDao {    void say();}// 接口实现类UserDao1public class UserDaoImpl1 implements UserDao {    @Override    public void say() {        System.out.println("I am userDao1");    }}// 接口实现类UserDao2public class UserDaoImpl2 implements UserDao {    @Override    public void say() {        System.out.println("I am userDao2");    }}// UserService中进行调用public class UserService {    public void getUser(UserDao userDao){        userDao.say();    }}

在上面的代码中,我们可以看到,UserService类中getUser方法参数可以是UserDao1类型的,也可以是UserDao2类型的,不像之前的代码,只能是指定的UserDao。这时,UserService和UserDao1、UserDao2联系的就没那么紧密了,这是一种弱耦合关系,通过接口来进行解耦。

但是仔细查看上面的代码,你会发现,接口和实现类之间还是存在强耦合关系,在面向接口编程中,我们常常会看到类似这样的代码:

UserDao userDao = new UserDaoImpl1();

假设现在不用这个UserDaoImpl1了,而改用UserDao的另一个实现类UserDaoImpl2,代码就要改为如下:

UserDao userDao = new UserDaoImpl2();

这样也就是接口和实现类出现了耦合,为了进一步解耦,我们就使用下面的工厂模式。

3. 工厂模式解耦

工厂的意思也就是一个批量制造同样规格(规格也就是接口类所提供好的规范)类的类,所谓的工厂模式也就是将所有的创建对象任务交给了一个“中间人”,也就是工厂类来实现,要想使用对象,直接找工厂类,实现类必须要从工厂中取出来。对工厂模式有疑问的可以参考我之前的文章:Java 中设计模式 之 工厂模式

而要使用工厂模式进行解耦,我们需要先将创建对象交给工厂类:

// 工厂类public class BeanFactory {    // 创建并返回UserDaoImpl1    public static UserDao getUserDao1(){        return new UserDaoImpl1();    }    // 创建并返回UserDaoImpl2    public static UserDao getUserDao2(){        return new UserDaoImpl2();    }}

将创建对象交给工厂类,调用关系就转变为如下:

UserDao userDao = new UserDaoImpl1();  ===>  UserDao userDao1 = BeanFactory.getUserDao1();UserDao userDao = new UserDaoImpl2();  ===>  UserDao userDao2 = BeanFactory.getUserDao2();

这样一来,我们创建对象只需要调用工厂类BeanFactory中的方法即可,调用时不是直接通过接口,而是通过工厂类,将创建对象交给了工厂类,就降低了接口和实现类之间的耦合。

上面的方法虽然降低了接口和实现类之间的耦合度,但是,这样接口和工厂类之间就产生了耦合,为了再次解耦,我们引入了反射+xml配置文件的方式进行再次解耦。

4. xml 配置 + 反射 +  工厂解耦(IoC底层的实现)

使用xml配置文件

<bean id="userDao" class="**.UserDaoImpl">

工厂类

class BeanFactory {    public static UserDao getUserDao(String id) {        // String className = 解析配置文件xml 拿到id对应的class        // 反射        class clazz = class.forName(className);        return clazz.newInstance();    }}

可以看到,在这个工厂类中,并没有直接像上面那样直接new对象,而是使用了xml解析和反射方式创建对象,分析如下:

  • 通过xml解析获取对象中属性的值

  • 通过反射得到字节码文件

  • 通过字节码文件创建对象

这样的话如果我们需要改UserDao的实现类的类型,我们可以直接在配置文件中修改,就不需要修改代码,这就是IoC的解耦。

三、IoC 原理理解

以下部分是开涛这位技术牛人对Spring框架的IOC的理解,写得非常通俗易懂,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846

1. IoC是什么

IoC:Inversion of Control(控制反转),这不是什么技术,而是一种设计思想,在java开发中,IoC意味着将你设计好的对象交给容器,而不是传统的在你的对象内部直接控制,如何理解好Ioc呢?理解好IoC的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由IoC容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)

  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

2. IoC能做什么

IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

  IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

3. IoC和DI

DI:DI—Dependency Injection,即“依赖注入”组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

  理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器

  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源

  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象

  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

  IoC和DI有什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

为了更好的理解,我找了一个例子:

一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者)

在原始社会里,几乎没有社会分工;需要斧子的人(调用者)只能自己去磨一把斧子(被调用者);对应情形为:Java程序里的调用者自己创建被调用者,通常采用new关键字调用构造器创建一个被调用者

进入工业社会,工厂出现了,斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程;对应简单工厂设计模式,调用者只需定位工厂,无须管理被调用者的具体实现

进入“共产主义”社会,需要斧子的人甚至无须定位工厂,“坐等”社会提供即可;调用者无须关心被调用者的实现,无须理会工厂,等待Spring依赖注入

总之依赖注入的意思是你需要的东西不是由你创建的,而是第三方,或者说容器提供给你的。这样的设计符合正交性,即所谓的松耦合。

IoC的底层原理就到这里,下一篇将讲述AOP的底层原理


底层原理_Spring框架底层原理IoC相关推荐

  1. @autowired注解原理_Spring框架第二谈:IOC,xml配置文件给属性赋值,注解实现赋值...

    在spring的对象文件中,给Java对象的属性赋值: di依赖注入,表示创建对象,给属性赋值di的实现方法有两种:1.在spring的配置文件中,使用标签完成,叫做基于XML的di实现2.使用spr ...

  2. spring aop实现原理_Spring 异步实现原理与实战分享

    最近因为全链路压测项目需要对用户自定义线程池 Bean 进行适配工作,我们知道全链路压测的核心思想是对流量压测进行标记,因此我们需要给压测的流量请求进行打标,并在链路中进行传递,那么问题来了,如果项目 ...

  3. php框架laravel原理,Laravel框架运行原理

    写在前面: 使用任何框架,如果理解该框架原理,应用起来会更加得心应手. 一.生命周期 1. 入口文件: Laravel框架所有请求入口统一进入/public/index.php文件,请求通过Ngxin ...

  4. 底层框架_你有必要了解一下Flink底层RPC使用的框架和原理

    1. 前言 对于Flink中各个组件(JobMaster.TaskManager.Dispatcher等),其底层RPC框架基于Akka实现,本文着重分析Flink中的Rpc框架实现机制及梳理其通信流 ...

  5. 微信小程序底层框架实现原理

    小册介绍 小程序(Mini Program)我们都很熟悉,它是一种不用下载安装就能使用的应用,它实现了应用"触手可及"的梦想.如今,微信已经把小程序打造成了新的开发者生态,而小程序 ...

  6. Java集合框架底层原理

    Java集合框架 Java集合框架 List集合 ArrayList底层实现原理 ArrayList数组扩容技术(数组拷贝) 扩容大小 查询和删除 集合中的泛型 LinkedList Vector 线 ...

  7. Kingfisher框架底层原理

    Kingfisher框架底层原理 一, Kingfisher框架概述 二,Kingfisher框架流程图 一, Kingfisher框架概述 Kingfisher 是一个异步下载.缓存网络图片的轻量级 ...

  8. iOS 底层探索篇 —— KVC 底层原理

    iOS 底层探索篇 -- KVC 底层原理 1. Method Swizzling的坑与应用 1.1 method-swizzling 是什么? 1.2 坑点 坑点1:method-swizzling ...

  9. mysql引擎层存储层_MySQL存储底层技术:InnoDB底层原理解读

    原标题:MySQL存储底层技术:InnoDB底层原理解读 存储引擎 很多文章都是直接开始介绍有哪些存储引擎,并没有去介绍存储引擎本身.那么究竟什么是存储引擎?不知道大家有没有想过,MySQL是如何存储 ...

最新文章

  1. BZOJ1747 [Usaco2005 open]Expedition 探险
  2. 神策用户标签系统,深入业务构建用户价值体系
  3. SpringBoot 嵌入式Servlet容器
  4. 修改了系统时间后,myeclipse 和tomcat下的代码不同步了
  5. android 布局之scrollview
  6. aggregations 详解1(概述)
  7. Java Servlet教程– ULTIMATE指南(PDF下载)
  8. Linux网络编程“惊群”问题总结
  9. leetCode 题 - 100. 相同的树
  10. 机器视觉:PC式视觉系统与嵌入式视觉系统区别
  11. Linux命令发送Http请求
  12. 跨浏览器(IE/FF/OPERA)JS代码小结
  13. java程序员_java程序员这个职业赚钱吗,看一线程序员怎么说
  14. python的作用域分别有几种_Python作用域和命名空间
  15. php soap header_PHP调用有SoapHeader认证的WebService实例
  16. jenkins下载安装及环境搭建
  17. Unix.Trojan.Agent-37008木马查杀
  18. java+vue实现onlyoffice协同办公
  19. 云脉H5高效纠错系统
  20. 【云计算】1_云计算基础介绍

热门文章

  1. 第四天2017/03/31(下午2:结构体、数组)
  2. Java SE7新特性之try-with-resources语句
  3. 简明 Python 编程规范v2
  4. 程序员面试题精选100题(60)-判断二叉树是不是平衡[数据结构]
  5. Coursera课程Python for everyone:chapter6
  6. Octave相关学习资源整理出
  7. 编程之美-寻找最大的k个数
  8. 《大话数据结构》第2章 算法基础 2.9 算法的时间复杂度
  9. Vue+axios配置踩坑记录
  10. 安装完CentOS可以不做的事