写点什么

Spring 框架底层原理 -IoC,java 架构师指南 pdf 下载

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:3230 字

    阅读完需:约 11 分钟

// 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();


}


?


// 接口实现类 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 和 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 的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:



    《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
    浏览器打开:qq.cn.hn/FTe 免费领取
    复制代码


    谁控制谁,控制什么:传统 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 容器

    用户头像

    Java高工P7

    关注

    还未添加个人签名 2021.11.08 加入

    还未添加个人简介

    评论

    发布
    暂无评论
    Spring框架底层原理-IoC,java架构师指南pdf下载