1.Spring框架详解
1.1 框架的概述
1.1.1 为什么要使用框架技术
现在我们已经能够完成数据库数据到页面的数据操作了,那么为什么还要学习框架技术呢?
举个例子
如何更快更好地写简历? 使用word简历模板!
其实我们拿一个文本文档也能写一份简历,为什么要用word简历模板呢?
对比之下我们不难发现,word模板还是很耐看的!!! 所以总结模板对干一件事的重要性
总结来看,写个简历尚且如此,如果写代码不用个模板的话,为了写好我们的代码,我们也可以找各种各样的模板来更好的写代码;
软件设计中 根据不同的软件操作 肯定也有不同的模板支持 那么我们统一把这些个帮助我们完成不同编码模板的技术–> 框架技术
1.1.2 什么是框架技术
框架技术
是一个应用程序的半成品
提供可重用的公共结构
按一定规则组织的一组组件
分析优势
不用再考虑公共问题
专心在业务实现上
结构统一,易于学习、维护
新手也可写出好程序
注意 :
框架的产生是为了我们能够解决同类型一些任务中的共同问题,而且框架的产生最早起源于建筑行业,建筑工程师发现他们每次盖楼其实结构都差不多,只是最后外观或者房屋细节
有所区别,所以他们为了每次不重复的设计大楼,就出现了现在的钢筋或凝土结构的大楼框架,有了它以后盖一个新楼就可以在这个基础上进行设计改造即可,既省时又省力;
HTML --> JSP:
java程序 --> jdbc --> DbUtils --> Servlet --> 数据库 --> mysql X
后来软件设计中也引入了这一思想,所以才有了现在的框架技术;
1.1.3 主流框架介绍
1) 在Servlet的基础上优化而来的框架
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
2) 在JDBC的基础上优化而来的框架
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate
可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。
2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
3) 一个容器级别的重要框架
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。
Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
SSM框架 spring springMVC mybatis --> SSS spring springMVC springdata --> spring boot springmvc springdata jpa springcloud
4) 我们能够涉及的
我们接下来要围绕这Spring展开学习,所以Servlet技术我们就可以用SpringMVC来优化了,
而数据层照顾到我们的SQL一般以统计居多,所以很难做到面向对象操作,所以dbutls已经是一个很好的JDBC工具了,所以此处我们还是采用DBUtils
那么也就是说 我们未来会采用spring+springMVC+dbutils的形式来开发我们数据库表的CURD操作;
5表 --5个结果
5 --> 联合 --> 一个结果
SSD 框架 spring springMVC Dbuits
TNB框架
1.2 框架应用实例
1.2.1 框架应用经典实例 : 软件设计中的耦合问题
a. 软件的耦合 :
简单地说,软件工程中对象之间的耦合度就是对象之间的依赖性。指导使用和维护对象的主要问题是对象之间的多重依赖性。
对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。
耦合性是程序结构中各个模块之间相互关联的度量。它取决于各个模块之间的接口的复杂程度、调用模块的方式以及哪些信息通过接口。
b. 常见的耦合:(了解)
1) 内容耦合:
最常见的一种耦合,写写代码瞬间内容耦合
内容耦合是指如果一个模块与另一个模块的内部属性有关,不经调用直接使用另一个模块的程序代码或内部数据,那么这两个模块之间就存在内容耦合。
2) 公共耦合:最常见的另一种耦合,写写代码瞬间公共耦合
公共耦合指通过一个公共数据环境相互作用的那些模块间的耦合.公共数据环境可以是全程变量或数据结构共享的通信,内存的公共覆盖区及任何存储介质上的文件,
物理设备等(也有将共享外部设备分类为外部耦合).由于两个模块都要引用同一个公共数据域,因此有较高的耦合度。
3) 控制耦合/外部耦合 :
特别常见的两种组合耦合形式,写写代码突然之间控制耦合,写写代码顺其自然的就外部耦合了
一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
4) 标记耦合 :
特别常见的一种耦合方式,写写代码必然标记耦合
若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
5) 数据耦合 :
耦合中最合理的耦合形式,写写代码分分钟数据耦合模块之间通过参数来传递数据,那么被称为数据耦合。
数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
6) 非直接耦合 : 也是一种常见的耦合形式,写写代码偷摸就耦合了
两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。
总结:
通过以上案例我们可以看出,耦合在一起的模块,如果一方发生了变化肯定会影响另一方,这样的话独立性降低危险度提高,维护成本自然就高了,
所以说耦合是我们软件编程行业的一个很大的敌人!
原则:如果必须用耦合的话,尽量使用数据耦合,少用控制耦合,限制公共耦合范围,避免内容耦合;
在程序设计的时候 一个类 与 一个类之间尽量少发生关联
c. 耦合的解决方案 : 模块内聚完成解耦和;
说明: 高内聚低耦合是我们软件设计的最终解决思路,但是也不要迷信,高内聚意味着编码成本的增加,所以完美的高内聚是不存在的,所以适度设计即可;
耦合 : 影响软件质量和稳定性的因素 解决耦合 内聚 但是内聚成本很高
正常来讲 完全的内聚是不可能的 作为软件系统中的模块会或多或少的和别的模块发生关联
我们只要求一件事 如果模块之间有关联那么应该是最小关联
举例 : 比如说 你 和 空气 , 你依赖空气 如果想解耦和 自己生产空气
虽然说咱软件领域崇尚的低耦合 高内聚 高内聚的代价就是编程成本的上升
编写期间 让代码 进行的保持独立(内聚的体现) 在程序运行期间 再把代码关联起来(耦合)
1.1.2 假如收现在有这么一段代码
我们创建:
实体类:
package com.hnxy.entity;
/**
* 用户表的实体类
* @author sysmaster
*
*/
public class UserInfo {
// 私有属性
private Integer id; // 编号
private String name; // 姓名
// 共有方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "UserInfo [id=" + id + ", name=" + name + "]";
}
}
DAO :
package com.hnxy.dao;
import com.hnxy.entity.UserInfo;
/**
* DAO层实现类
* @author sysmaster
*
*/
public interface UserDAO {
/**
* 根据ID查询数据
* @param id
* @return
* @throws Exception
*/
public UserInfo findUserByID(Integer id)throws Exception;
}
实现类 :
package com.hnxy.dao.impl;
import com.hnxy.dao.UserDAO;
import com.hnxy.entity.UserInfo;
public class UserDAOImpl implements UserDAO {
@Override
public UserInfo findUserByID(Integer id) throws Exception {
// 创建方法的返回值
UserInfo user = new UserInfo();
user.setId(1);
user.setName("赵文明");
// 返回
return user;
}
}
Service :
package com.hnxy.service;
import com.hnxy.entity.UserInfo;
public interface UserService {
/**
* 根据ID查询数据
* @param id
* @return
* @throws Exception
*/
public UserInfo findUserByID(Integer id)throws Exception;
}
实现类:
package com.hnxy.service.impl;
import com.hnxy.dao.UserDAO;
import com.hnxy.dao.impl.UserDAOImpl;
import com.hnxy.entity.UserInfo;
import com.hnxy.service.UserService;
public class UserServiceImpl implements UserService {
// 创建DAO层对象
private UserDAO userDAO = new UserDAOImpl();
@Override
public UserInfo findUserByID(Integer id) throws Exception {
return userDAO.findUserByID(id);
}
}
测试类:
package com.hnxy.test;
import com.hnxy.entity.UserInfo;
import com.hnxy.service.UserService;
import com.hnxy.service.impl.UserServiceImpl;
public class Test1 {
public static void main(String[] args) {
try {
UserService service = new UserServiceImpl();
UserInfo user = service.findUserByID(1);
System.out.println(user);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果 :
####1.1.3 分析这段代码可知
在service层 我们创建的DAO层对象,在test类中创建service层的对象都会产生耦合的情况,所以我们为了避免这种耦合我们可以采取的办法就是不创建对象
比如这样
package com.hnxy.test;
import com.hnxy.entity.UserInfo;
import com.hnxy.service.UserService;
import com.hnxy.service.impl.UserServiceImpl;
public class Test1 {
public static void main(String[] args) {
try {
UserService service = null;
UserInfo user = service.findUserByID(1);
System.out.println(user);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
但是这样又会有一个问题是,没有实例对象我们无法进行方法的调用
所以这时候我们就要去思考了我们这个实例对象该用还是要用,但是类与类之间的又不能发生耦合现象,那么这个时候该怎么办呢?
此时我们的框架对比如下所示:
1.2.2 XML概述
根据我们上一个案例的分析其实不难发现,在案例中如果不想让两个类耦合的话最好的办法就是不要在类型引用其他类的对象,因为这些类可能会发生改变影响其他类,
也就是我们说的耦合,那么此时我们就会思考解决耦合的办法有哪些?冷静思考之后我们不难发现反射可以解决这个问题
public static void main(String[] args) {
try {
// 反射创建userDAO对象
UserDAO userDAO = (UserDAO)Class.forName("com.hnxy.dao.impl.UserDAOImpl").newInstance();
System.out.println(userDAO);
} catch (Exception e) {
e.printStackTrace();
}
}
此处我们把本来的类与类之间的耦合降低为类与字符串之间的耦合,这样虽然能帮我们降低类与之间的耦合性,但是还是有个问题不容忽视
我们把要创建对象的类的权限定名写到了这个java文件里,那也就是意味着如果我要修改这个创建对象的类名的话必须把当前这个java文件
重新编译,那么这样就意味着我要付出相当大的代价来改变原来的代码,所以这时候我就要想我能不能把这些经常改变的东西拿到别的地方去配置 而且也不用编译就好使呢?
答案是肯定的!此时你需要一个XML作为你的配置文件
1.3 XML 可扩展标记语言
可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。
在电子计算机中,标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如文章等。
它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。
是Internet环境中跨平台的、依赖于内容的技术,也是当今处理分布式结构信息的有效工具。早在1998年,W3C就发布了XML1.0规范,使用它来简化Internet的文档信息传输。
1.3.1 语法 :
头部标记 :
<?xml version="1.0" encoding="UTF-8"?>
单标签 :
<标签名称 属性1="值1" 属性2="值2" .... 属性N="值N" />
双标签 :
<标签名称 属性1="值1" 属性2="值2" .... 属性N="值N">标签之间的内容</标签名称>
特点 :
因为是程序的元语言文件,XML是不需要任何编译器就能直接被计算机识别,其实我们在之前就已经体会过了,我们现在写的HTML就是XML的一种高级应用,我们写HTML的时候只
需要一个记事本就可以,写完了无需编译直接扔到浏览器里运行即可,所以我们可以利用XML的特性把要创建对象的类以某种自定义格式扔到XML中,然后用Java技术读取XML
读取到之后再反射生成对象,这样的话我们就再也不用有编译的烦恼了;
那XML怎么创建呢?
在src包上点击鼠标右键 选择"new" --> “other”
在弹出的对话框 输入 xml 然后选择下面的 XML File选项 点击 “next”
在弹出的页面中我们填写XML文件的文件名 applicationContext.xml
点击 Finish 完成XML的创建
编写需要的数据
现在我们的设定是在程序中不能new 类的对象,那么这样的情况下我们就把要创建对象的类放在XML中然后我们读取XML中的类用反射在程序运行期间创建对象
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDAO" class="com.hnxy.dao.impl.UserDAOImpl" />
<bean id="userService" class="com.hnxy.service.impl.UserServiceImpl" />
</beans>
开始读取XML的内容
此处解析XML我们使用的是DOM4J工具
1.4 dom4j
dom4j是一个Java的XML API,是jdom的升级品,用来读写XML文件的。dom4j是一个十分优秀的JavaXML API,具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的dom技术,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。
在IBM developerWorks上面还可以找到一篇文章,对主流的Java XML API进行的性能、功能和易用性的评测,所以可以知道dom4j无论在哪个方面都是非常出色的。
如今可以看到越来越多的Java软件都在使用dom4j来读写XML,特别值得一提的是连Sun的JAXM也在用dom4j。这已经是必须使用的jar包, Hibernate也用它来读写配置文件。
DOM4J使用以下jar包用来解析XML
具体即系步骤 :
// 获取XML的位置
InputStream in = this.getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
try {
// 开始解析
// 1. 将XML加载到内存中 这个步骤相当于把字节流转换为内存中的XML文件
Document document = new SAXReader().read(in);
// 2. 获取XML的根节点 beans
Element root = document.getRootElement();
// 3. 找到根节点下的所有bean的子节点
List<Element> elements = root.elements("bean");
// 4. 解析其中的id属性和value属性
for (Element element : elements) {
// 获取ID属性
String id = element.attributeValue("id");
String clazz = element.attributeValue("class");
System.out.println(id+":"+clazz);
}
} catch (DocumentException e) {
e.printStackTrace();
}
看到这个结果其实我们应该能想到一个问题,如果我们那类名创建对象 然后拿ID值作为key的话,将这些创建的对象再保存到一个Map集合里去
程序用到的时候用map的key获取某个对象这样我们就不用new这个对象了
这个工具类可以通过读取XML的形式创建其他类的对象,也就是说创建所有类的对象都可以通过这一个类来创建,那么这个专门用来创建对象的类就是对象工厂类
而这种设计模式被叫做简单工厂设计模式
简单工厂模式获取目标对象有什么好处呢?使用工厂模式隐藏创建对象细节,这样可以统一对象管理,如果要修改某些实例对象只需要修改工厂方法即可,这样引用工厂的那些个类都不用修改,
package com.hnxy.utils;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class BeanFactory {
// 定义对象保存容器
private static Map<String, Object> objs = new HashMap<String, Object>();
static {
// 获取XML的位置
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.xml");
try {
// 开始解析
// 1. 将XML加载到内存中 这个步骤相当于把字节流转换为内存中的XML文件
Document document = new SAXReader().read(in);
// 2. 获取XML的根节点 beans
Element root = document.getRootElement();
// 3. 找到根节点下的所有bean的子节点
List<Element> elements = root.elements("bean");
// 4. 解析其中的id属性和value属性
for (Element element : elements) {
// 获取ID属性
String id = element.attributeValue("id");
String clazz = element.attributeValue("class");
// 通过类名创建对象
Object obj = Class.forName(clazz).newInstance();
// 保存到MAP集合
objs.put(id, obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBean(String id) {
return objs.get(id);
}
public static void main(String[] args) {
System.out.println(BeanFactory.getBean("userDAO"));
}
}
1.5 使用自定义IOC框架
我们已经编写完了自己的创建对象的工具类,这样如果哪个地方需要这个对象我们就可以调用方法获取这个对象了
1.5.1 自定义IOC总结
以前 UserDAO
UserDAO userDAO = new UserDAOImpl(); --> new 产生一个新对象
现在 UserDAO XML --> 工厂 --> UserDAO (解耦和)
UserDAO对象举例
UserDAOImpl --> 类可定有创建对象的能力 --> new UserImpl() 这种方案容易耦合
UserDAOImpl --> 工厂的一个方法 --> 创建对象 这种方案不容易耦合
所以我们一般都要用工厂这种方式进行解耦和而这种方式叫做 控制反转的方式进行解耦和
什么是控制反转?Inversion of Control,缩写为IoC
对象的创建权限 由 本类 交给了工厂创建,对这种对象创建权限的移交 --> 控制反转(IOC)
总结来看 :
1) 什么是IOC?–>控制反转,把类创建自己对象的权利交给框架,让框架统一管理对象;
2) 为什么要用IOC创建对象呢?–>解耦和,IOC能把两个类的硬耦合(编程阶段耦合在了一起)编程两个类的软耦合(程序运行期间耦合在了一起),这样互不干扰,特别开心;
3) 在哪用IOC(控制反转)?–>框架应用中使用比较频繁
3) 怎么用?–> 1)定义对象容器 2)工厂模式获取对象
IOC作用 : 消除计算机程序对象的耦合
1.7.知识回顾
JSP+Servlet技术
a) 已学习技术
JSP 小脚本 Model1 优点 简单 直接 易用 缺点 不能够有效整合和提取代码 每次在创建新的项目必须要从头开始写
Servlet Model2 开发模式 注重 请求与相应模式 优点 : 代码更加标准化 缺点 : 复杂 而且代码量偏多 重复代码很多
b) 需要掌握的
能够熟练编写Servlet程序完成数据的CURD
能够熟练的使用JSP的主要内置对象完成需求的编写
能够简单理解Servlet执行流程和过滤器的使用 Servlet 每次只处理一个请求 /finfUserListByPageAction /updateUserAction 过滤器 /* 设置编码规则 —> 交给Servlet
c) 小测试
- 请说一说你对JSP和Servlet(MVC C控制器小程序)的理解与它们之间的联系
- 请说出JSP的几个常用内置对象
- 分层开发的好处是什么 ?
根据不同的操作进行不同层次编写
最底层 DAO(内有任何逻辑思维 只完成SQL语句的执行)
中间层 Service(主要处理业务逻辑 负责保证DAO的正常运行)
控制层 web/controller( 负责控制 负责接收页面请求 然后 它决定了 service 应该调用什么DAO完成什么操作 它是一个程序的核心领导者)