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中根据请求路径来选择对应的处理方法,

? 当然如何将路径匹配到对应的方法也有不同的方法

  1. 直接在Servlet中判断路径然后调用方法,(比较low)
  2. 通过反射,查找与路径匹配的方法(方法名称相同,局限)
  3. 通过反射+注解,方法名称可以随意,用注解提供路径即可(灵活,高端)

SpringMVC采用的就是第三种方法

是不是想自己写一个MVC框架? 加油吧骚年

原文地址:https://www.cnblogs.com/yangyuanhu/p/12053840.html

时间: 2024-10-12 18:53:55

JavaWeb中的MVC 下的相关文章

JavaWeb学习过程 之MVC模式下的查询

近些天一直在学习javaweb 但总是时断时续,今天终于有时间,来学习一下MVC下的查询模式. 一.首先先了解一下 什么是mvc? MVC是Model-View-Controller的简称,即模型-视图-控制器.它是一种设计模式,它吧应用程序分成三个核心模块,模型,视图,控制器.他们各自处理自己的任务. 模型:是应用程序的主体部分,模型指的是业务模型. 一个模型可以为多个视图提供数据. 视图:是用户看到的并与之交互的界面. 可以向用户显示相关的数据,接受用户的输入,但是它不进行任何实际的业务处理

个人对JavaWeb中MVC与三层架构的区别理解

JavaWeb中MVC是指B/S结构开发中的一种设计模式,能够将Web页面与业务逻辑以及数据库操作之间的耦合性降到最低点,易于代码的重用以及后期维护.而三层架构的概念在JavaWeb中则是指Web层,业务层,数据层.但三层架构中却是把MVC中的Model,也就是JavaBeans分成业务层和数据层,而把Controller控制器以及View视图合并成Web层.二者之间最大的区别在于MVC只限于JavaWeb,而三层架构则能够实现在非Web应用平台之上,只需替换掉Web层足以,而业务层与数据层依旧

MVC的设计模式在JavaWeb中的实现

JSP开发模式 jsp开发模式的发展 1.模式1:(适合小型项目的技术的开发) a.第一版本,纯jsp(封装数据,处理数据,显示数据) b.第二版本,Jsp+JavaBean. jsp:收集数据,显示数据 JavaBean:封装.处理 2.模式2:servlet+Jsp+JavaBean(是mvc在java中的具体的实现,是java技术实现的具体的内容) a.servlet:负责协调jsp和javabean,获得数据,处理数据(业务逻辑),封装到javabean中,选择jsp去显示数据. b.j

javaWeb中的文件上传下载

在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件.这个common-fileupload上传组件的jar包可以去apache官网上面下载,也可以在struts的lib文件夹下面找到,stru

javaweb中静态文件的处理方法

方案一:激活Tomcat的defaultServlet来处理静态文件 在web.xml中添加: <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-

MVC下微信企业号网页应用开发调用JSSDK接口不成功问题

在MVC下进行企业微信里的网页应用开发,前台页面中为了调用图像接口,使用了JSSDK.按照官方文档进行了正确配置. 现象:WEB开发工具调试一切正常,但从手机企业微信应用进入页面后接口调用无效(这个页面是从一级页面链接过来的),开了调试模式后,显示config:ok.说明配置无误.经过反复检查,终于发现问题所在. 原因:在一级页面(http://xx.xxx.xxx/main/index)中的链接是使用htmlhelper来写的,即@Html.ActionLink("测试接口",&qu

ASP.NET MVC下的四种验证编程方式[续篇]

在<ASP.NET MVC下的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注ValidationAttribute特性"."让数据类型实现IValidatableObject或者IDataErrorInfo"),那么在ASP.NET MVC框架内部是如何提供针对这四种不同编程方式的支持的呢?接下来我们就来聊聊这背后的故事. 一.ModelValidator与ModelVal

【干货理解】理解javascript中实现MVC的原理

理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法.模型有对数据直接访问的权利.模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作. 视图:视图层最主要的是监听模型层上的数据改变,并且实时的更新html页面.当然也包括一些事件的注册或者aja

简单谈谈js中的MVC

MVC是什么? MVC是一种架构模式,它将应用抽象为3个部分:模型(数据).视图.控制器(分发器). 本文将用一个经典的例子todoList来展开(代码在最后). 一个事件发生的过程(通信单向流动): 1.用户在视图 V 上与应用程序交互 2.控制器 C 触发相应的事件,要求模型 M 改变状态(读写数据) 3.模型 M 将数据发送到视图 V ,更新数据,展现给用户 在js的传统开发模式中,大多基于事件驱动的: 1.hash驱动 2.DOM事件,用来驱动视图 3.模型事件(业务模型事件和数据模型事