写点什么

Spring - IOC

用户头像
小马哥
关注
发布于: 2021 年 02 月 01 日
Spring - IOC

知识点-工厂模式解耦

1.目标


  • 能够使用工厂模式进行解耦


2.路径


  1. 程序耦合的概述


  1. 使用工厂模式解耦

3.讲解

3.1 程序的耦合

3.1.1 程序耦合的概述


​ 耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。 耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。

​ 在软件工程中, 耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计

应使类和构件之间的耦合最小。 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 划分模块的一个

准则就是高内聚低耦合。

总结:

​ 耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。内聚与耦合内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件

结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之

间的相互依存度却要不那么紧密。


​ 内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他

模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合。


3.1.2 在代码中体现


​ 早期我们的 JDBC 操作(面向接口的编程),注册驱动时,我们为什么不使用 DriverManager 的 register 方法,而是采

用 Class.forName 的方式?


​ 原因就是: 我们的类依赖了数据库的具体驱动类(MySQL) ,如果这时候更换了数据库(比如 Oracle) ,

需要修改源码来重新数据库驱动。这显然不是我们想要的。


/*** 程序的耦合* 耦合:程序间的依赖关系* 包括:* 		类之间的依赖* 		方法间的依赖* 解耦:* 		降低程序间的依赖关系* 实际开发中:* 		应该做到:编译期不依赖,运行时才依赖。* 解耦的思路:*		第一步:使用反射来创建对象,而避免使用 new 关键字。*		第二步:通过读取配置文件来获取要创建的对象全限定类名*/public class JdbcDemo1 {    public static void main(String[] args) throws Exception{        //1.注册驱动         //DriverManager.registerDriver(new Driver());//第一种注册驱动的方式        //第一种注册驱动的方式,缺点是耦合性强                Class.forName("com.mysql.jdbc.Driver");//这是第二种注册驱动的方式        //第二种注册驱动的方式,缺点是"字符串硬编码",可以使用配置文件解决                //2.获取连接        //3.获取操作数据库的预处理对象        //4.执行 SQL,得到结果集        //5.遍历结果集        //6.释放资源    }}
复制代码


第一种耦合:一个类直接依赖另外一个类

第二种耦合:将字符串直接写死在 Java 代码中(字符串的硬编码)

3.1.3 解决程序耦合的思路

产生类与类之间的耦合的根本原因是:在一个类中 new 了另外一个类的对象

解决耦合的思路:不在类中创建另外一个类的对象,但是我们还需要另一个类的对象;由别人(Spring)把那个类的对象创建好之后给我用就可以了

​ 当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:

Class.forName("com.mysql.jdbc.Driver");//此处只是一个字符串

​ 此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运行就不要想了没有驱动不可能运行成功的) 。

​ 同时,也产生了一个新的问题, mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。解决这个问题也很简单,使用配置文件配置

3.2.自定义 IOC(工厂模式解耦)


  1. 原始方式

+ 方式: 创建类, 直接根据类 new 对象

+ 优点: 好写, 简单

+ 缺点: 耦合度太高, 不好维护

  1. 接口方式

+ 方式: 定义接口, 创建实现类. 接口=子类的对象

+ 优点: 耦合度相对原始方式 减低了一点

+ 缺点: 多写了接口, 还是需要改源码 不好维护

  1. 自定义 IOC

+ 方式: 使用对象的话, 不直接 new()了,直接从工厂里面取; 不需要改变源码

3.2.1 原始方式

代码略

3.2.2 接口方式
  • UserService.java



public interface UserService { String getName();}
复制代码


  • UserServiceImpl.java



public class UserServiceImpl implements UserService{ public void init(){ System.out.println("UserServiceImpl对象创建了..."); }
public void destroy(){ System.out.println("UserServiceImpl对象销毁了..."); }
@Override public String getName() { return "周杰棍"; }}
复制代码


  • TestSpring.java


public class TestSpring {    public void test01() {        AccountService accountService = new AccountServiceImpl();        accountService.getName();    }
}
复制代码


3.2.3 自定义 IOC(使用工厂模式解耦)


3.2.2.1 工厂模式解耦思路


​ 在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候, 让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件, 创建和获取三层对象的类就是工厂

3.2.2.2 实现
  • 添加坐标依赖


<dependencies>    <dependency>        <groupId>junit</groupId>        <artifactId>junit</artifactId>        <version>4.12</version>        <scope>test</scope>    </dependency>
<!-- 解析 xml 的 dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!-- dom4j 的依赖包 jaxen --> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1-beta-8</version> </dependency></dependencies>
复制代码


  • UserService.java



public interface UserService { String getName();}
复制代码


  • UserServiceImpl.java



public class UserServiceImpl implements UserService{ public void init(){ System.out.println("UserServiceImpl对象创建了..."); }
public void destroy(){ System.out.println("UserServiceImpl对象销毁了..."); }
@Override public String getName() { return "周杰棍"; }}
复制代码


  • Client.java



public class UserServiceImpl implements UserService{ @Override public String getName() { return "张三"; }}
复制代码


  • BeanFactory.java


下面的通过 BeanFactory 中 getBean 方法获取对象就解决了我们代码中对具体实现类的依赖。


import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;
import java.io.InputStream;import java.util.HashMap;import java.util.List;import java.util.Map;

public class BeanFactory { private static Map<String,Object> map = new HashMap(); static { try { //将读取配置文件的代码,放到静态代码块中 //解析配置文件beans.xml,根据id的值,获取class的值 SAXReader saxReader = new SAXReader(); InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"); Document document = saxReader.read(is); //获取所有的bean标签 List<Element> elementList = document.selectNodes("//bean"); if (elementList != null) { for (Element element : elementList) { //获取标签的id属性 String idAttr = element.attributeValue("id"); String className = element.attributeValue("class"); //根据类的全限定名,创建出该类的对象 Object obj = Class.forName(className).newInstance(); //以id作为key,以对象作为value存入map中 map.put(idAttr,obj); } } } catch (Exception e) { e.printStackTrace(); } } public static Object getBean(String id){ try { //根据传入的id,获取到对应的className Object obj = map.get(id); if (obj != null) { return obj; } throw new RuntimeException("找不到对应的类!!!"); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } }}
复制代码


  • spring.xml


<?xml version="1.0" encoding="UTF-8"?><beans>    <bean class="com.nengli51.service.impl.UserServiceImpl" id="userService"></bean></beans>
复制代码


4.小结


  1. 程序初始化的时候, 解析 xml, 全部解析好 存到容器【Map】里面

  2. 把 bean 标签的 id 作为 map 的 key, bean 标签的 class 属性值反射创建对象之后作为 map 的 value

  3. 要使用对象, 不再直接 new 了, 而是从容器里面直接取

知识点-IOC 概念

1.目标

  • [ ] 能够理解 IOC 概念

2.路径


  1. 分析和 IOC 的引入

  2. IOC 概述

3.讲解

3.1 分析和 IOC 的引入


上一小节我们通过使用工厂模式,实现了解耦。

它的核心思想就是:

​ 1、通过 Bean 工厂读取配置文件使用反射创建对象。

​ 2、把创建出来的对象都存起来,当使用者需要对象的时候,不再自己创建对象,而是调用 Bean 工厂的方法从容器中获取对象

这里面要解释两个问题:

​ 第一个:存哪去?

​ 分析:由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。

​ 到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。

​ 所以我们的答案就是:在应用加载时,创建一个 Map,用于存放三层对象。我们把这个 map 称之为容器。

​ 第二个: 什么是工厂?

​ 工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。

​ 原来:我们在获取对象时,都是采用 new 的方式。 是主动的。

3.2IOC 概述


​ IOC(inversion of control)的中文解释是“控制反转”,对象的使用者不是创建者. 作用是将对象的创建 反转给 spring 框架来创建和管理。


​ 控制反转怎么去理解呢。 其实它反转的是什么呢,是对象的创建工作。 举个例子:平常我们在 servlet 或者 service 里面创建对象,都是使用 new 的方式来直接创建对象,现在有了 spring 之后,我们就再也不 new 对象了,而是把对象创建的工作交给 spring 容器去维护。我们只需要问 spring 容器要对象即可


​ ioc 的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。


4.小结


  1. IOC: 控制反转. 对象的使用者不是创建者. 把对象的创建和管理, 交给 Spring 框架. 要使用的时候, 直接从 Spring 工厂(容器)里面取

  2. 作用: 解耦.


发布于: 2021 年 02 月 01 日阅读数: 43
用户头像

小马哥

关注

自强不息,厚德载物 2018.12.22 加入

像一棵竹子那样, 不断的扎根积累, 活出节节高的人生!

评论

发布
暂无评论
Spring - IOC