欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaWeb中的MVC 下

程序员文章站 2022-07-04 22:46:22
代码较多,请先略过代码,看懂逻辑在研究代码 引入 回顾上一节中的项目,最终的层次结构: 在MVC上中,我们分析了MVC设计模式具备的优点,以及不足,并在其基础上增了Service层用于处理业务逻辑,但是这还没完,对于大型项目来说,程序结构依然是不够清晰的,Service层不仅要处理业务逻辑,还要处理 ......

代码较多,请先略过代码,看懂逻辑在研究代码

引入

回顾上一节中的项目,最终的层次结构:

JavaWeb中的MVC 下

在mvc上中,我们分析了mvc设计模式具备的优点,以及不足,并在其基础上增了service层用于处理业务逻辑,但是这还没完,对于大型项目来说,程序结构依然是不够清晰的,service层不仅要处理业务逻辑,还要处理数据库操作;依然存在以下问题:

  • 耦合度高
  • 代码复用性差
  • service层的功能职责不单一

dao

为了解决这些问题,需要将service层进一步的解耦,如何做到呢,通过增加数据访问层dao即可实现;

定义:

dao,(data access object),数据访问对象,指提供了对数据的crud功能的对象;,负责与数据库打交道,处于业务逻辑层和数据库之间;

简单的说:

就是把原本在service层中的数据库操作封装到dao层中去;使得service层可以专注于业务逻辑的处理,降低各功能间的耦合度;

增加dao后的层次结构图:

JavaWeb中的MVC 下

dao层的组成

dao是针对数据访问层的设计模式,其完整组成包括如下部分:

JavaWeb中的MVC 下
看起来非常复杂,没错,dao是一个用于解决数据库操作的完整解决方案,如果按照上述结构来实现的话,对于大型商业项目而言非常的规范,但是对小型的需要,快速开发的项目而言,让人望而生畏

老司机建议: 过度设计会让人看不清本质,学习设计模式时一定要牢记你要解决的关键问题,带着问题去看各部分的作用和重要性

实例

为了能够更清晰的认识到dao的本质,这里采用简化后的dao设计

dao中有三个对象是必须的:

  • 数据访问对象(bean)
  • 数据库连接类
  • dao实现类

最终service层需要的是dao实现对象,数据库连接对象是为了将重复代码进行抽取而存在的,bean也是在原来系统中已经存在的

现在需要两个新的类一个dao,一个数据库连接类,创建它们像下面这样

JavaWeb中的MVC 下

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执行流程:

JavaWeb中的MVC 下

工厂和接口呢?

设计模式就像是把双刃剑,带来了扩展性,维护性,等等优势等的同时,设计模式的加入也会使程序变得更加复杂

我们要做的是在合适的项目中采用合适的设计模式

上述案例中没有使用工厂和接口,那啥时候用呢?

分析:

当项目发展到后期,公司赚到大钱了,想要替换更强大的oracle数据库,oracle的sql有细微的差别,于是不得不重新写一套新的dao实现,写完后你又不得不查找所有使用了userdao的地方,全部修改一遍,你第一次哭出声来...

JavaWeb中的MVC 下

聪明的你,不会让自己再遇到这种情况,于是你就........辞职了!

总结:

由于两者之间方法全都一样,仅仅是sql语句不同,所以为它们抽取一个接口,再定义一个用于创建对象的工厂类,今后要更换实现类的时候,修改工厂类即可更换dao的实现类

整体结构如图:

JavaWeb中的MVC 下

如果最后在提供一个配置文件,让factory到配置文件中获取需要创建的dao类型,一切就完美了!

到这里mvc+service+dao的设计模式就完事了

一个servlet处理多个不同功能的请求

等等,我隐约记得第一篇中的什么问题没解决?

JavaWeb中的MVC 下

这个问题其实解决方案很简单:

​ 先将需要处理的请求映射到servlet,然后在servlet中根据请求路径来选择对应的处理方法,

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

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

springmvc采用的就是第三种方法

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