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.kkb.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.kkb.models; import com.kkb.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.kkb.srvices; import com.kkb.exceptions.loginexception; import com.kkb.models.userbean; import com.kkb.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框架? 加油吧骚年
推荐阅读
-
Linux系统下中 在命令行中实现Wifi 连接的方法
-
Linux系统下如何检测并修复bash中的破壳漏洞
-
Asp.net Core MVC中怎么把二级域名绑定到特定的控制器上
-
6. ASP.NET MVC 5.0 中的HTML Helper【HTML 帮助类】
-
JQuery中Ajax的Post提交在IE下中文乱码的解决方法
-
棘轮效应下,囚徒困境中的智能穿戴设备该何去何从?
-
.Net MVC网站中配置文件的读写
-
win8下XAMPP中Apache模块无效(apache无法打开)的解决方法
-
Asp.Net MVC中Aplayer.js音乐播放器的使用
-
ASP.NET Core MVC 中实现中英文切换的示例代码