Spring框架底层原理-IoC

1、概述

  • Spring是一个轻量级的开源JavaEE框架
  • Spring能够解决企业应用开发的复杂性
  • Spring两大核心部分:IoC和AOP
  • 特色:
    • 方便解耦,简化开发
    • AOP编程支持
    • 方便程序测试
    • 方便和其余框架整合
    • 方便事务操做
    • 下降API开发难度

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

2、IoC解耦推导

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

所谓解耦就是下降类之间的依赖和方法之间的依赖设计模式

1. 传统直接调用对象

在咱们传统的开发方式中,是直接采起new对象的方式建立对象,常常能够看到service调用dao这样的代码,若是是直接调用,咱们来看看是怎么样子的,建立UserService和UserDao ,经过UserService调用UserDao:markdown

// 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中的代码,万一,忽然,产品经理想把UserDao三、UserDao四、UserDao5.....这样具备相同功能的类也在userService中做为参数进行调用,新建这些类倒还好,避免不了,问题是还要修改UserService类,简直头大....ide

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

2. 接口解耦

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

// UserDao接口
public interface UserDao {
    void say();
}

// 接口实现类UserDao1
public class UserDaoImpl1 implements UserDao {
    @Override
    public void say() {
        System.out.println("I am userDao1");
    }
}

// 接口实现类UserDao2
public 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和UserDao一、UserDao2联系的就没那么紧密了,这是一种弱耦合关系,经过接口来进行解耦。spa

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

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的解耦。

3、IoC 原理理解

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

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的底层原理

相关文章
相关标签/搜索