java学习day36-三大框架-Spring框架--简介/程序耦合解耦-BeanFactory模式

spring框架

spring简介

什么是Spring?

spring是分层的JavaSE及JavaEE应用于全栈的轻量级开源框架,以IoC(Inverse Of Control:控制反转/反转控制)和AOP(Aspact Oriented Programming:面向切面编程)为核心,提供了表现层SpringMVC和持久层Spring
JDBC以及业务层事务管理等众多模块的企业级应用技术,还能整合开源世界中众多著名的第三方框架和类库,逐渐成为使用最多的JavaEE企业应用开源框架。

SSH(struts2 spring hibernate)

SSM(springmvc spring mybatis)

Spring的本质是管理软件中的对象,即创建对象维护对象之间的关系

Spring的发展历程

1997 年 IBM提出了EJB 的思想

1998 年,SUN制定开发标准规范 EJB1.0

1999 年,EJB1.1 发布

2001 年,EJB2.0 发布

2003 年,EJB2.1 发布

2006 年,EJB3.0 发布

Rod Johnson (罗德·约翰逊,spring 之父)

Expert One-to-One J2EE Development without EJB(2004)

阐述了 J2EE 开发不使用 EJB的解决方式(Spring 雏形)

2017年9月份发布了spring的最新版本spring 5.0通用版

Spring的优势

1).方便解耦,简化开发

通过 Spring提供的 IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为较为底层的需求编写代码,可以更专注于上层的应用。

2).AOP 编程的支持

通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP(Object Oriented Programming:面向对象编程) 实现的功能可以通过 AOP 轻松应付。

3).声明式事务的支持

可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

4).方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

5).方便集成各种优秀框架

Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。

6).降低 JavaEE API 的使用难度。

Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API 的使用难度大为降低。

7).Spring框架源码是经典学习范例

Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对 Java技术的高深造诣。它的源代码无疑是Java技术的最佳实践的范例。

spring的架构

Spring 最初的目标就是要整合一切优秀资源,然后对外提供一个统一的服务。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

模块 说明
核心容器Spring Core 核心容器,提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式,将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring Context Spring上下文,是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP 通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。可以很容易地使 Spring框架管理的任何对象支持AOP。Spring AOP模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,就可以将声明性事务管理集成到应用程序中。
Spring DAO JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring Web Web上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以Spring 框架支持与 Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC框架 MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

程序中的耦合和解耦

什么是程序的耦合

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

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

降低程序之间的依赖程度,即降低程序之间的耦合度的过程就叫做解耦。

例如:早期的Jdbc操作中,在注册数据库驱动时,为什么采用的是Class.forName的方式,而不是采用DriverManager.registerDriver的方式?

public class TestJdbc {
  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.释放资源
  }
}

除了DriverManager.registerDriver会导致驱动注册两次外,更重要的是,如果使用这种方式,JDBC程序就会依赖于数据库的驱动类(MySQL的Driver类),如果后期程序因数据量和性能原因升级到Oracle数据库,就需要修改程序源代码——重新导入新的驱动类,这会增加很多不必要的麻烦!

而是用Class.forName方式注册驱动,这样的好处是Jdbc程序不再依赖具体的驱动类,即使删除(或不导入)mysql驱动包,程序依然可以编译(当然不可能运行,因为运行时肯定需要依赖驱动)。

此时类中仅仅是将mysql驱动类的全限定类名写死在程序中(只是一个字符串),可以将这个字符串提取到配置文件中,后期可以通过修改配置文件(而不用修改程序代码)轻松的替换数据库产品。

工厂模式解耦介绍

在实际开发中可以将三层(表现层、业务层、持久层)的对象都使用配置文件配置起来,当启动服务器加载应用时,可以通过工厂读取配置文件,根据配置文件中的配置将这些对象创建出来,在接下来使用的时候,直接拿过来使用即可。

那么,这个负责读取配置文件,根据配置文件创建并返回这些对象的类就是工厂。

可以通过【工厂+接口+配置文件】的方式解除程序中的耦合。

工厂模式解耦示例


解耦程序编写步骤:


1、创建一个Maven的Java工程(day16-spring)

2、创建持久层接口和接口实现类

com.tedu.dao.EmpDao (接口)

com.tedu.dao.EmpDaoImpl (实现类)

3、创建业务层接口和接口实现类

com.tedu.service.EmpService (接口)

com.tedu.service.EmpServiceImpl (实现类)

4、创建表现层测试程序(com.tedu.controller.EmpController)并运行测试程序

5、通过工程+配置文件+接口(已有)方式解耦

(1)创建工厂类(com.tedu.factory.BeanFactory)并实现

(2)提供配置文件,将service接口和dao接口的实现类的全限定类名编写到配置文件中。

6、使用工厂获取service接口和dao接口的实例,替换使用new的方式获取接口的实例。


详细代码如下:


1、创建持久层接口(com.tedu.dao.EmpDao)

package com.tedu.dao;
/**
 * 员工模块的Dao(持久层)接口
 */
public interface EmpDao {
	/** 添加员工信息 */
	public void addEmp();
}

2、创建持久层接口实现类(com.tedu.dao.EmpDaoImpl)

package com.tedu.dao;
/**
 * 员工模块的Dao(持久层)接口实现类
 */
public class EmpDaoImpl implements EmpDao {
	@Override
	public void addEmp() {
		System.out.println(
			"Dao层的addEmp()方法执行了..成功保存了一条员工信息.."
		);
	}
}

3、创建业务层接口(com.tedu.service.EmpService)

package com.tedu.service;
/**
 * 员工模块的service(业务层)接口
 */
public interface EmpService {
	/** 添加员工信息 */
	public void addEmp();
}

4、创建业务层接口实现类(com.tedu.service.EmpServiceImpl)

package com.tedu.service;
import com.tedu.dao.EmpDao;
import com.tedu.dao.EmpDaoImpl;
/**
 * 员工模块的service(业务层)接口实现类
 * service层 ---> dao层
 */
public class EmpServiceImpl implements EmpService {
	/* 获取Dao接口的子类实例
	 * ——这里使用new对象的方式造成了程序之间的耦合性提升 */
	private EmpDao dao = new EmpDaoImpl();
	
	@Override
	public void addEmp() {
		System.out.println("调用dao层的方法添加员工信息...");
		dao.addEmp();
	}
}

5、创建表现层测试类(com.tedu.controller.EmpController)

package com.tedu.controller;
import org.junit.Test;
import com.tedu.service.EmpService;
import com.tedu.service.EmpServiceImpl;
/**
 * 模拟表现层 
 * controller --> service --> dao
 */
public class EmpController {
	/* 获取Service接口的子类实例
	 * ——这里使用new对象的方式造成了程序之间的耦合性提升 */
	private EmpService service = new EmpServiceImpl();
	
	@Test
	public void testAddEmp() {
		System.out.println("调用service层的方法添加员工信息...");
		service.addEmp();
	}
}

在上面的程序中,EmpController中要调用Service层的方法,所以通过new对象的形式获取了EmpService接口子类的实例,代码如下:

private EmpService service = new EmpServiceImpl();

在EmpService的实现类中要调用Dao层的方法,所以通过new对象的形式获取了EmpDao接口子类的实例,代码如下:

private EmpDao dao = new EmpDaoImpl();

如果在上面的程序中将EmpDaoImpl或者EmpServiceImpl移除,会导致其他类中的代码编译错误。此时表现层和业务层,及业务层和持久层之间的依赖程度过高,如果将来替换某一层,很可能会造成其他层无法运行,只能通过修改程序代码保证程序运行,这样依赖就会提高维护成本以及造成不必要的麻烦。

而在程序中new对象的方式造成了这种程序之间的依赖程度提升,即提升了程序之间的耦合性。

使用工厂+配置文件+接口解耦代码如下:


6、创建com.tedu.factory.BeanFactory类,用于创建各个层所需要的对象。

package com.tedu.factory;

import java.io.InputStream;
import java.util.Properties;
/**
 * ---------------------------------------------------------
 * Bean: 可重用组件(计算机英语)
 * JavaBean:使用Java语言编写的可重用组件,例如:service层、dao层等
 * JavaBean:通常分为业务Bean和实体Bean
 * 		业务Bean:处理业务逻辑,service层、dao层
 * 		实体Bean:封装数据,例如,为了封装员工信息而编写的Emp实体类.
 * ---------------------------------------------------------
 * 解除耦合:
 * (1)需要提供配置文件,在配置文件中配置service和dao的实现类
 * 		配置内容为:唯一标识=实现类的全限定类名(key=value结构)
 * (2)通过工厂读取配置文件中配置的全限定类名,利用反射创建对象。
 * 		xml配置文件、properties配置文件
 */
public class BeanFactory {
	//声明一个Properties对象,在静态代码块中对其进行初始化
	private static Properties prop;
	static {
		try {
			//为prop进行实例化
			prop = new Properties();
			//获取配置文件的流对象
			InputStream in = BeanFactory.class.getClassLoader()
					.getResourceAsStream("config.properties");
			//将配置文件中的内容读取到Properties对象中
			prop.load( in );
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("初始化Properties对象失败!");
		}
	}
	
	/**
	 * 根据config.xml文件中的key获取对应class类的实例
	 * @param key 
	 * @return
	 */
	public static Object getBean(String key) {
		Object bean = null;
		try {
			String className = prop.getProperty( key );
			bean = Class.forName(className).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bean;
	}
}

7、在源码目录下创建一个config.properties文件,文件内容配置如下:

EmpService=com.tedu.service.EmpServiceImpl
EmpDao=com.tedu.dao.EmpDaoImpl

8、将EmpController类中通过 "new对象的形式获取了EmpService接口子类的实例" 以及在EmpServiceImpl类中通过 "new对象的形式获取了EmpDao接口子类的实例" 改为使用BeanFactory工厂获取Service和Dao层的实例。如下:

/* 获取Service接口的子类实例
 * ——这里使用new对象的方式造成了程序之间的耦合性提升 */
//private EmpService service = new EmpServiceImpl();
private EmpService service = (EmpService)BeanFactory.getBean("EmpService");
/* 获取Dao接口的子类实例
 * ——这里使用new对象的方式造成了程序之间的耦合性提升 */
//private EmpDao dao = new EmpDaoImpl();
private EmpDao dao = (EmpDao)BeanFactory.getBean( "EmpDao" );

注:此模式下是多例模式

工厂模式的升级版

package com.tedu.factory;

import java.io.InputStream;
import java.util.Properties;

 
public class BeanFactory {
	//声明一个Properties对象,在静态代码块中对其进行初始化
	private static Properties prop;
    //定义一个容器,用来存放所有的对象
    Map<String,Object> beans ;
	static {
		try {
			//为prop进行实例化
			prop = new Properties();
            //初始化容器
            map = new HashMap<String,Object>();
			//获取配置文件的流对象
			InputStream in = BeanFactory.class.getClassLoader()
					.getResourceAsStream("config.properties");
            //将配置文件中的内容读取到Properties对象中
			prop.load( in );
            //取出配置文件中的所有的key 返回枚举类型
            Enumeration<String> keys = prop.keys();
            //遍历这个枚举
            while(keys.hasMoreElements()){
                //取出每一个key
                String key = keys.nextElement().toString();
                //根据可以取出value
                String beanpath = prop.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanpath).newInstance();
                //将key和value存入map中
                beans.put(key,value);
            }
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("初始化Properties对象失败!");
		}
	}
	
	/**
	 * 根据config.xml文件中的key获取对应class类的实例
	 * @param key 
	 * @return
	 */
	public static Object getBean(String key) {
		return beans.get(key);
	}
}

此工厂就是单例模式了,执行效率高

猜你喜欢

转载自www.cnblogs.com/liqbk/p/13172740.html