JavaWeb中的MVC 下
代码较多,请先略过代码,看懂逻辑在研究代码
引入
回顾上一节中的项目,最终的层次结构:
在MVC上中,我们分析了MVC设计模式具备的优点,以及不足,并在其基础上增了Service层用于处理业务逻辑,但是这还没完,对于大型项目来说,程序结构依然是不够清晰的,Service层不仅要处理业务逻辑,还要处理数据库操作;依然存在以下问题:
- 耦合度高
- 代码复用性差
- Service层的功能职责不单一
DAO
为了解决这些问题,需要将Service层进一步的解耦,如何做到呢,通过增加数据访问层DAO即可实现;
定义:
DAO,(Data Access Object),数据访问对象,指提供了对数据的CRUD功能的对象;,负责与数据库打交道,处于业务逻辑层和数据库之间;
简单的说:
就是把原本在Service层中的数据库操作封装到DAO层中去;使得Service层可以专注于业务逻辑的处理,降低各功能间的耦合度;
增加DAO后的层次结构图:
DAO层的组成
DAO是针对数据访问层的设计模式,其完整组成包括如下部分:
看起来非常复杂,没错,DAO是一个用于解决数据库操作的完整解决方案,如果按照上述结构来实现的话,对于大型商业项目而言非常的规范,但是对小型的需要,快速开发的项目而言,让人望而生畏
老司机建议: 过度设计会让人看不清本质,学习设计模式时一定要牢记你要解决的关键问题,带着问题去看各部分的作用和重要性
实例
为了能够更清晰的认识到DAO的本质,这里采用简化后的DAO设计
DAO中有三个对象是必须的:
- 数据访问对象(Bean)
- 数据库连接类
- DAO实现类
最终Service层需要的是DAO实现对象,数据库连接对象是为了将重复代码进行抽取而存在的,Bean也是在原来系统中已经存在的
现在需要两个新的类一个DAO,一个数据库连接类,创建它们像下面这样
DBTool:
数据库连接类,负责连接数据库执行查询,最后关闭资源
package com.yyh.test;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DBTool {
//默认参数
public String ip = "127.0.0.1";
public int port = 3306;
public String
user="root",
password="admin",
charset ="utf8",
dbName="db1";
private static boolean DriverLoaded=false;
//使用默认参数链接数据库
public DBTool() throws ClassNotFoundException {
if(DriverLoaded)return;
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("DBTools message:数据库驱动加载成功!");
} catch (ClassNotFoundException e) {
System.out.println("DBTools Error:驱动程序加载失败!");
throw e;
}
DriverLoaded=true;
}
//自定义参数初始化
public DBTool(String ip, int port, String user, String password, String dbName) throws ClassNotFoundException {
this();
this.ip = ip;
this.port = port;
this.user = user;
this.password = password;
this.dbName = dbName;
}
//自定义参数初始化
public DBTool(String user, String password, String dbName) throws ClassNotFoundException {
this();
this.user = user;
this.password = password;
this.dbName = dbName;
}
//获取一个链接
public Connection getConnection() throws SQLException {
String url = String.format("jdbc:mysql://%s:%s/%s?characterEncoding=%s&user=%s&password=%s&useSSL=false",ip,port,dbName,charset,user,password);
try {
return DriverManager.getConnection(url);
} catch (SQLException e) {
System.out.println("DBTools Error 数据库连接失败!");
throw e;
}
}
//执行查询语句
public List<Map<String,Object>> executeQuery(String sql, Object...args) throws SQLException {
ArrayList<Map<String, Object>> res = new ArrayList<>();
ResultSet resultSet = null;
PreparedStatement preparedStatement = null;
Connection connection = null;
try {
connection = getConnection();
preparedStatement = getPreparedStatement(connection, sql, args);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
resultSet.getMetaData().getColumnCount();
HashMap<String, Object> map = new HashMap<>();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount() ; i++) {
map.put(resultSet.getMetaData().getColumnName(i),resultSet.getObject(i));
}
res.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
throw e;
} finally {
if(resultSet != null)
resultSet.close();
if(preparedStatement != null)
preparedStatement.close();
if(connection != null)
connection.close();
}
return res;
}
//sql参数预处理
private PreparedStatement getPreparedStatement(Connection connection, String sql, Object[] args) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(sql);
int count = sql.length() - sql.replace("?", "").length();
if(count != args.length){
throw new SQLException("DBTool Error: 参数个数不匹配");
}
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i+1,args[i]);
}
return preparedStatement;
}
//执行更新语句
public boolean executeUpdate(String sql,Object...args) throws SQLException {
try {
Connection connection = getConnection();
PreparedStatement preparedStatement = getPreparedStatement(connection, sql, args);
int i = preparedStatement.executeUpdate();
if (i>0){return true;}
} catch (SQLException e) {
e.printStackTrace();
throw e;
}
return false;
}
}
UserDao:
负责为Service层提供需要的CURD方法,本质就是封装了SQL的执行,和结果的解析
package com.yyh.models;
import com.yyh.test.DBTool;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class UserDao {
private DBTool tools;
//构造函数
public UserDao() throws ClassNotFoundException {
tools = new DBTool();
}
//新增用户
public boolean insertUser(UserBean user) throws SQLException {
String sql = "insert into user values(null,?,?)";
return tools.executeUpdate(sql, user.getName(), user.getPwd());
}
//删除用户
public boolean deleteUser(UserBean user) throws SQLException {
String sql = "delete from user where id = ?";
return tools.executeUpdate(sql, user.getId());
}
//更新用户
public boolean updateUser(UserBean user) throws SQLException {
String sql = "update user set name = ? , pwd = ? where id = ?";
return tools.executeUpdate(sql, user.getName(), user.getPwd(), user.getId());
}
//查询所有用户
public List<UserBean> queryAllUser() throws SQLException {
ArrayList<UserBean> beans = new ArrayList<>();
List<Map<String, Object>> maps = tools.executeQuery("select *from user");
//转List
for (Map<String, Object> temp : maps) {
UserBean bean = getUserBean(temp);
beans.add(bean);
}
return beans;
}
//map 转 bean 方法
private UserBean getUserBean(Map<String, Object> temp) {
UserBean bean = new UserBean();
bean.setId((Integer) temp.get("id"));
bean.setName((String) temp.get("name"));
bean.setPwd((String) temp.get("pwd"));
return bean;
}
//通过ID查询
public UserBean queryUserByID(Integer id) throws SQLException {
List<Map<String, Object>> maps = tools.executeQuery("select *from user where id = ?", id);
//转List
for (Map<String, Object> temp : maps) {
UserBean bean = getUserBean(temp);
return bean;
}
return null;
}
//登录认证
public UserBean checkLogin(UserBean login) throws SQLException {
List<Map<String, Object>> maps = tools.executeQuery("select *from user where name = ? and pwd = ?", login.getName(), login.getPwd());
for (Map<String, Object> temp : maps) {
UserBean bean = getUserBean(temp);
return bean;
}
return null;
}
//通过名字查询
public UserBean queryUserByName(String name) throws SQLException {
String sql = "select *from user where name = ?";
List<Map<String, Object>> maps = tools.executeQuery(sql, name);
if (!maps.isEmpty()){
return getUserBean(maps.get(0));
}
return null;
}
}
UserService:
替换原来使用JDBC的地方,改为使用UserDao来完成数据库操作
package com.yyh.srvices;
import com.yyh.exceptions.LoginException;
import com.yyh.models.UserBean;
import com.yyh.models.UserDao;
import java.sql.*;
//用来处理与用户相关的业务逻辑
public class UserService {
private UserDao dao;
public UserService() throws ClassNotFoundException {
this.dao = new UserDao();
}
//用于检查登录的方法
public UserBean checkLogin(UserBean reqBean) throws LoginException {
//判断参数是否有效
if(reqBean.getName() == null || reqBean.getPwd() == null ||
reqBean.getName().equals("") || reqBean.getPwd().equals("")
){
throw new LoginException("用户名或密码不能为空!");
}else {
try {
UserBean bean = dao.checkLogin(reqBean);
if(bean != null)
return bean;
else
throw new LoginException("用户名或密码错误!");
} catch (SQLException e) {
e.printStackTrace();
throw new LoginException("数据库炸了!");
}
}
}
}
如此,就利用DAO(数据访问对象),来对Service层进行了解耦合,代码的可维护性提高了,但是相应的程序的复杂度也提高了
强调:
DAO中的方法不是固定的要根据具体业务需求来设计
MVC+DAO执行流程:
工厂和接口呢?
设计模式就像是把双刃剑,带来了扩展性,维护性,等等优势等的同时,设计模式的加入也会使程序变得更加复杂
我们要做的是在合适的项目中采用合适的设计模式
上述案例中没有使用工厂和接口,那啥时候用呢?
分析:
当项目发展到后期,公司赚到大钱了,想要替换更强大的Oracle数据库,oracle的sql有细微的差别,于是不得不重新写一套新的DAO实现,写完后你又不得不查找所有使用了UserDao的地方,全部修改一遍,你第一次哭出声来...
聪明的你,不会让自己再遇到这种情况,于是你就........辞职了!
总结:
由于两者之间方法全都一样,仅仅是SQL语句不同,所以为它们抽取一个接口,再定义一个用于创建对象的工厂类,今后要更换实现类的时候,修改工厂类即可更换Dao的实现类
整体结构如图:
如果最后在提供一个配置文件,让Factory到配置文件中获取需要创建的Dao类型,一切就完美了!
到这里MVC+Service+DAO的设计模式就完事了
一个Servlet处理多个不同功能的请求
等等,我隐约记得第一篇中的什么问题没解决?
这个问题其实解决方案很简单:
先将需要处理的请求映射到Servlet,然后在Servlet中根据请求路径来选择对应的处理方法,
当然如何将路径匹配到对应的方法也有不同的方法
直接在Servlet中判断路径然后调用方法,(比较low)
通过反射,查找与路径匹配的方法(方法名称相同,局限)
通过反射+注解,方法名称可以随意,用注解提供路径即可(灵活,高端)
SpringMVC采用的就是第三种方法
是不是想自己写一个MVC框架? 加油吧骚年
JavaWeb中的MVC 下的更多相关文章
- JavaWeb中的MVC
不使用什么MVC的案例分析: 利用Servlet与jsp实现登陆请求,数据库查询,以及页面的跳转逻辑 具体流程如下: 不做任何结构上的考虑,可以简单的做如下实现: 目录结构 LoginServlet ...
- MVC下 把数据库中的byte[]值保存成图片,并显示在view页面
MVC下 把数据库中的byte[]值转成图片,并显示在view页面 controller中的action方法 //显示图片[AllowAnonymous]public ActionResult Sho ...
- MVC的设计模式在JavaWeb中的实现
JSP开发模式 jsp开发模式的发展 1.模式1:(适合小型项目的技术的开发) a.第一版本号,纯jsp(封装数据.处理数据,显示数据) b.第二版本号,Jsp+JavaBean. ...
- javaWeb中的文件上传下载
在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用 ...
- 转:攻击JavaWeb应用[5]-MVC安全
转:http://static.hx99.net/static/drops/tips-347.html 攻击JavaWeb应用[5]-MVC安全 园长 · 2013/07/25 13:31 注:这一节 ...
- MVC下压缩输入的HTML内容
在MVC下如何压缩输出的HTML代码,替换HTML代码中的空白,换行符等字符? 1.首先要了解MVC是如何输出HTML代码到客户端的,先了解下Controller这个类,里面有很多方法,我们需要的主要 ...
- ASP.NET MVC下的四种验证编程方式[续篇]
在<ASP.NET MVC下的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注Validation ...
- ASP.NET MVC下的四种验证编程方式
ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定 ...
- MVC下判断PC和移动端
MVC下的PC端和移动端,其实没区别,写法都一样,只是有两点才改变了它们 第一点:就是单击这个页面任何地方的时候判断是移动端还是客户端: 第二点:新建手机端区域Areas(简单来说:Areas就相当于 ...
随机推荐
- hdu 1166 敌兵布阵 (线段树、单点更新)
敌兵布阵Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...
- pat 1136 A Delayed Palindrome(20 分)
1136 A Delayed Palindrome(20 分) Consider a positive integer N written in standard notation with k+1 ...
- 从壹开始 [ Design Pattern ] 之一 ║ 设计模式开篇讲
缘起 不说其他的没用的开场白了,直接给大家分享三个小故事,都来自于我的读者粉丝(我厚着脸皮称为粉丝吧
- 将Swagger2文档导出为HTML或markdown等格式离线阅读
网上有很多<使用swagger2构建API文档>的文章,该文档是一个在线文档,需要使用HTTP访问.但是在我们日常使用swagger接口文档的时候,有的时候需要接口文档离线访问,如将文档导 ...
- Android、IOS的Fiddler证书安装教程
通过手机连接fiddler实现代理访问,其中必须得安装证书才能生效,那么针对不同的手机连接方式也不一样,分为两类:Android.iOS: Android-Fiddler证书安装: 直接在任意浏览器中 ...
- LaravelS - 基于Swoole加速Laravel/Lumen
LaravelS LaravelS是一个胶水项目,用于快速集成Swoole到Laravel或Lumen,然后赋予它们更好的性能.更多可能性.Github 特性 内置Http/WebSocket服务器 ...
- 在React旧项目中安装并使用TypeScript的实践
前言 本篇文章默认您大概了解什么是TypeScript,主要讲解如何在React旧项目中安装并使用TypeScript. 写这个的目的主要是网上关于TypeScript这块的讲解虽然很多,但都是一些语 ...
- vue JS实现监听浏览器返回按键事件
// 这个是监听浏览器回退键的returnButton () { let vm = this; $(document).ready(function () { if (window.history & ...
- SpringBoot第一次案例
一.Spring Boot 入门 1.Spring Boot 简介 简化Spring应用开发的一个框架: 整个Spring技术栈的一个大整合: J2EE开发的一站式解决方案: 2.微服务 2014,m ...
- 抖音抖一抖-SVG和CSS视觉故障艺术小赏
故障艺术,英文名称叫glitch,在很多赛博朋克作品中经常看到,其实就是故意表现一种显示设备的小故障效果,抖音的图标其实就是这种的效果,我们看下这个图标 这个图标中的红色和蓝色的偏移其实就是一种故障艺 ...