在实际开发中咱们能够把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法经过读取配置文件,把这些对象建立出来并存起来。在接下来的使用的时候,直接拿过来用就行了。 那么,这个读取配置文件,建立和获取三层对象的类就是工厂。java
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类建立型模式。在简单工厂模式中,能够根据参数的不一样返回不一样类的实例。简单工厂模式专门定义一个类来负责建立其余类的实例,被建立的实例一般都具备共同的父类。简单点说就是用来建立具备相同基类的对象mysql
简单工厂模式最大的优势在于实现对象的建立和对象的使用分离,将对象的建立交给专门的工厂类负责,可是其最大的缺点在于工厂类不够灵活,增长新的具体产品须要修改工厂类的判断逻辑代码,并且产品较多时,工厂方法代码将会很是复杂。 简单工厂模式适用状况包括:客户端只知道传入工厂类的参数,对于如何建立对象不关心;工厂类负责建立的对象比较少。spring
(划分模块的一个准则就是高内聚低耦合) 耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及经过界面传送数据的多少。sql
模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时代表其独立性越差( 下降耦合性,能够提升其独立性)。耦合性存在于各个领域,而非软件设计中独有的,可是咱们只讨论软件工程中的耦合。bash
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。所以对象的设计应使类和构件之间的耦合最小。 软件设计中一般用耦合度和内聚度做为衡量模块独立程度的标准。服务器
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上咱们应采用如下原则:若是模块间必须存在耦合,就尽可能使用数据耦合,少用控制耦合,限制公共耦合的范围,尽可能避免使用内容耦合。框架
<?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>
复制代码
一、当是咱们讲解 jdbc 时,是经过反射来注册驱动的,代码以下:Class.forName("com.mysql.jdbc.Driver");//此处只是一个字符串ui
二、此时的好处是,咱们的类中再也不依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然能够编译(运行就不要想了,没有驱动不可能运行成功的)。同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改仍是要修改源码。解决这个问题也很简单,使用配置文件配置。url
三、代码实现:持久层,业务层,变现层spa
当咱们讲解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个问题:
一、存哪去? 分析:因为咱们是不少对象,确定要找个集合来存。这时候有Map和List供选择。到底选Map仍是List就看咱们有没有查找需求。有查找需求,选Map。 因此咱们的答案就是: 在应用加载时,建立一个Map,用于存放action,Service和dao对象。咱们把这个map称之为容器。
二、仍是没解释什么是工厂? 工厂就是负责给咱们从容器中获取指定对象的类。这时候咱们获取对象的方式发生了改变。 原来,咱们在获取对象时,都是采用new的方式。是主动的。如今:咱们获取对象时,同时跟工厂要,有工厂为咱们查找或者建立对象是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。它的做用只有一个:削减计算机程序的耦合。