使用spring的IOC解决程序耦合的方法
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。 那么,这个读取配置文件,创建和获取三层对象的类就是工厂。
简单工厂模式(simple factory pattern):又称为静态工厂方法(static factory method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。简单点说就是用来创建具有相同基类的对象
简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。 简单工厂模式适用情况包括:客户端只知道传入工厂类的参数,对于如何创建对象不关心;工厂类负责创建的对象比较少。
1、程序的耦合
(划分模块的一个准则就是高内聚低耦合) 耦合性(coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。
模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd "> <!-- 定义userdaoimpl对象,并指定id为userdaoimpl --> <bean id="userdaoimpl" class="cn.bdqn.biz.dao.impl.userdaoimpl" /> <!-- 定义user对象,并指定id为user --> <bean id="user" class="cn.bdqn.biz.pojo.user" /> <!-- 定义userserviceimpl对象,并指定id为userserviceimpl --> <bean id="userserviceimpl" class="cn.bdqn.biz.service.impl.userserviceimpl"> <!--为userserviceimpl的userdao属性赋值,需要注意的是,这里要调用setuserdao()方法 --> <property name="userdao"> <!-- 引用id为userdao的对象为userserviceimpl的userdao属性赋值 --> <ref bean="userdaoimpl" /> </property> </bean> </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框架的核心之一。它的作用只有一个:削减计算机程序的耦合。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
无法找到产品Microsoft SQL Server Native Client的安装程序包的解决方法Sqlncli.msi
-
linuxmint使用root用户登录后声卡没有声音的解决方法
-
PyDev for Eclipse 无法正常使用的解决方法
-
微信小程序下拉刷新PullDownRefresh的使用方法
-
Spring Boot使用yml格式进行配置的方法
-
XP系统搜索功能不能用怎么办?XP系统搜索功能无法使用的解决方法
-
在spring中使用自定义注解注册监听器的方法
-
Spring Boot 与 Kotlin 使用Redis数据库的配置方法
-
解决ie以下网页失去响应 程序无响应关不掉的两种解决方法
-
chrome浏览器中无法使用QQ快速登录问题的解决方法