文档章节

使用 spring 的 IOC 解决程序耦合

 骚年锦时
发布于 05/19 17:05
字数 2493
阅读 2
收藏 0

工厂模式解耦

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

那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。简单点说就是用来创建具有相同基类的对象

简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。

简单工厂模式适用情况包括:客户端只知道传入工厂类的参数,对于如何创建对象不关心;工厂类负责创建的对象比较少。

 

1程序的耦合

划分模块的一个准则就是高内聚低耦合)

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。

模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。

在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。

软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。

 

耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。

 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xsi:schemaLocation="http://www.springframework.org/schema/beans
 5     http://www.springframework.org/schema/beans/spring-beans-3.1.xsd ">
 6     
 7     <!-- 定义userDaoImpl对象,并指定id为userDaoImpl -->
 8     <bean id="userDaoImpl" class="cn.bdqn.biz.dao.impl.UserDaoImpl" />
 9     <!-- 定义User对象,并指定id为user -->
10     <bean id="user" class="cn.bdqn.biz.pojo.User" />
11     
12     <!-- 定义UserServiceImpl对象,并指定id为userServiceImpl -->
13     <bean id="userServiceImpl" class="cn.bdqn.biz.service.impl.UserServiceImpl">
14         <!--为userServiceImpl的userDao属性赋值,需要注意的是,这里要调用setUserDao()方法 -->
15         <property name="userDao">
16             <!-- 引用id为userDao的对象为userServiceImpl的userDao属性赋值 -->
17             <ref bean="userDaoImpl" />
18         </property>
19     </bean>
20     
21 </beans>

 

2、解决程序耦合的思路

1、当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:Class.forName("com.mysql.jdbc.Driver");//此处只是一个字符串

2、此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运行就不要想了,没有驱动不可能运行成功的)。同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。解决这个问题也很简单,使用配置文件配置。

3、代码实现:持久层,业务层,变现层

 

当我们讲解jdbc时,是通过反射来注册驱动的,代码如下: Class.forName("com.mysql.jdbc.Driver");

这时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译。但是因为没有驱动类,所以不能运行。

不过,此处也有个问题,就是我们反射类对象的全限定类名字符串是在java类中写死的,一旦要改还是要修改源码。

解决这个问题也很简单,使用配置文件配置。

 

在实际开发中我们可以把所有的dao和service和action对象使用配置文件配置起来,当启动服务器应用加载的时候,通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。

 

package com.ioc.spring.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 获取某个包下面的所有类信息
 */
public class Util {

   /**
    * 取得某个接口下所有实现这个接口的类
    */
   public static List<Class> getAllClassByInterface(Class c) {
      List<Class> returnClassList = null;

      if (c.isInterface()) {
         // 获取当前的包名
         String packageName = c.getPackage().getName();
         // 获取当前包下以及子包下所以的类
         List<Class<?>> allClass = getClasses(packageName);
         if (allClass != null) {
            returnClassList = new ArrayList<Class>();
            for (Class classes : allClass) {
               // 判断是否是同一个接口
               if (c.isAssignableFrom(classes)) {
                  // 本身不加入进去
                  if (!c.equals(classes)) {
                     returnClassList.add(classes);
                  }
               }
            }
         }
      }

      return ClassList;
   }

   /*
    * 取得某一类所在包的所有类名 不含迭代
    */
   public static String[] getPackageAllClassName(String classLocation, String packageName) {
      // 将packageName分解
      String[] packagePathSplit = packageName.split("[.]");
      String realClassLocation = classLocation;
      int packageLength = packagePathSplit.length;
      for (int i = 0; i < packageLength; i++) {
         realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
      }
      File packeageDir = new File(realClassLocation);
      if (packeageDir.isDirectory()) {
         String[] allClassName = packeageDir.list();
         return allClassName;
      }
      return null;
   }

   /**
    * 从包package中获取所有的Class
    * @param packageName
    * @return
    */
   public static List<Class<?>> getClasses(String packageName) {

      // 第一个class类的集合
      List<Class<?>> classes = new ArrayList<Class<?>>();
      // 是否循环迭代
      boolean recursive = true;
      // 获取包的名字 并进行替换
      String packageDirName = packageName.replace('.''/');
      // 定义一个枚举的集合 并进行循环来处理这个目录下的things
      Enumeration<URL> dirs;
      try {
         dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
         // 循环迭代下去
         while (dirs.hasMoreElements()) {
            // 获取下一个元素
            URL url = dirs.nextElement();
            // 得到协议的名称
            String protocol = url.getProtocol();
            // 如果是以文件的形式保存在服务器上
            if ("file".equals(protocol)) {
               // 获取包的物理路径
               String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
               // 以文件的方式扫描整个包下的文件 并添加到集合中
               findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
            } else if ("jar".equals(protocol)) {
               // 如果是jar包文件
               // 定义一个JarFile
               JarFile jar;
               try {
                  // 获取jar
                  jar = ((JarURLConnection) url.openConnection()).getJarFile();
                  // 从此jar包 得到一个枚举类
                  Enumeration<JarEntry> entries = jar.entries();
                  // 同样的进行循环迭代
                  while (entries.hasMoreElements()) {
                     // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                     JarEntry entry = entries.nextElement();
                     String name = entry.getName();
                     // 如果是以/开头的
                     if (name.charAt(0) == '/') {
                        // 获取后面的字符串
                        name = name.substring(1);
                     }
                     // 如果前半部分和定义的包名相同
                     if (name.startsWith(packageDirName)) {
                        int idx = name.lastIndexOf('/');
                        // 如果以"/"结尾 是一个包
                        if (idx != -1) {
                           // 获取包名 把"/"替换成"."
                           packageName = name.substring(0, idx).replace('/''.');
                        }
                        // 如果可以迭代下去 并且是一个包
                        if ((idx != -1) || recursive) {
                           // 如果是一个.class文件 而且不是目录
                           if (name.endsWith(".class") && !entry.isDirectory()) {
                              // 去掉后面的".class" 获取真正的类名
                              String className = name.substring(packageName.length() + 1, name.length() - 6);
                              try {
                                 // 添加到classes
                                 classes.add(Class.forName(packageName + '.' + className));
                              } catch (ClassNotFoundException e) {
                                 e.printStackTrace();
                              }
                           }
                        }
                     }
                  }
               } catch (IOException e) {
                  e.printStackTrace();
               }
            }
         }
      } catch (IOException e) {
         e.printStackTrace();
      }

      return classes;
   }

   /**
    * 以文件的形式来获取包下的所有Class
    * 
    * @param packageName
    * @param packagePath
    * @param recursive
    * @param classes
    */
   public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
         List<Class<?>> classes) {
      // 获取此包的目录 建立一个File
      File dir = new File(packagePath);
      // 如果不存在或者 也不是目录就直接返回
      if (!dir.exists() || !dir.isDirectory()) {
         return;
      }
      // 如果存在 就获取包下的所有文件 包括目录
      File[] dirfiles = dir.listFiles(new FileFilter() {
         // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
         public boolean accept(File file) {
            return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
         }
      });
      // 循环所有文件
      for (File file : dirfiles) {
         // 如果是目录 则继续扫描
         if (file.isDirectory()) {
            findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
                  classes);
         } else {
            // 如果是java类文件 去掉后面的.class 只留下类名
            String className = file.getName().substring(0, file.getName().length() - 6);
            try {
               // 添加到集合中去
               classes.add(Class.forName(packageName + '.' + className));
            } catch (ClassNotFoundException e) {
               e.printStackTrace();
            }
         }
      }
   }
}

 

上面解耦的思路有2个问题:

1、存哪去?

分析:由于我们是很多对象,肯定要找个集合来存。这时候有Map和List供选择。到底选Map还是List就看我们有没有查找需求。有查找需求,选Map。

所以我们的答案就是:

在应用加载时,创建一个Map,用于存放action,Service和dao对象。我们把这个map称之为容器。

 

2、还是没解释什么是工厂?

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

原来,我们在获取对象时,都是采用new的方式。是主动的。现在:我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象是被动的。

这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。它的作用只有一个:削减计算机程序的耦合。

© 著作权归作者所有

粉丝 6
博文 28
码字总数 70387
作品 0
长沙
私信 提问
【Spring学习笔记】--IOC学习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/YYZZHC999/article/details/82824751 spring ioc原理 Ioc—Inversion of Control,即“控制反转”,不是什么技...

Hepburn_Yang
2018/09/23
0
0
Spring应用学习——IOC

Spring简介 1. Spring的出现是为了取代EJB(Enterprise JavaBean)的臃肿、低效、脱离现实的缺点。Spring致力于J2EE应用的各层(表现层、业务层、持久层)的解决方案,Spring是企业应用开发的...

江左煤郎
2018/11/16
0
0
spring学习3-Ioc和DI的简单介绍

控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转还有一个名字叫做依赖注入(Depende...

大嘴吃鸡腿
2014/03/16
0
0
控制反转和依赖注入的理解(通俗易懂)

学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一...

小橙子的曼曼
03/20
0
0
三分钟轻松了解Spring框架基础知识

做为一个合格的Java开发人员,你一定听说过Spring框架,虽然会应用,但却未必能够深入理解框架的原理,只是像其它人一样,泛泛的只是停留在理解的层面。小编会带着大家一步步了解Spring框架。...

爱尚实训
2018/07/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spark内置图像数据源初探

概述 在Apache Spark 2.4中引入了一个新的内置数据源, 图像数据源.用户可以通过DataFrame API加载指定目录的中图像文件,生成一个DataFrame对象.通过该DataFrame对象,用户可以对图像数据进行简...

阿里云官方博客
29分钟前
5
0
掌握Composer

这一次,真正掌握composer composer是现代PHP的基石 现代高级编程语言,依赖管理工具是必不可少的。Java有Maven,Python有pip,Nodejs有npm, 而在composer出现之前,PHP只有被广为诟病的Pea...

城市之雾
36分钟前
3
0
Shell中的函数、数组、告警系统

20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析 20.20 告警系统主脚本 20.21 告警系统配置文件 20.22 告警系统监控项目 20.23/20.24/20.25 告警系统邮件引擎 20.26 ...

tobej
36分钟前
3
0
Win7系统安装hadoop

环境准备 安装JDK1.8,配置JAVA_HOME 下载hadoop_3.1.2,配置HADOOP_HOME 配置HDFS 修改hadoop-env.cmd 增加 set HADOOP_PREFIX=%HADOOP_HOME%set HADOOP_CONF_DIR=%HADOOP_PREFIX%\etc\ha......

铲平王
40分钟前
2
0
IT兄弟连 Java语法教程 Java语言的其他特性

Java语言中除了非常重要的跨平台特性外,还有如下几个关键特性: ● 语法简单易学 Java语言的语法简单明了,容易掌握,而且是纯面向对象(OOP)的语言,Java语言的简单性主要体现在以下几个方...

老码农的一亩三分地
53分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部