MVC 三层架构案例详细讲解

科技资讯 投稿 6100 0 评论

MVC 三层架构案例详细讲解

MVC 三层架构案例详细讲解

目录
    MVC 三层架构案例详细讲解
    • 每博一文案
    • 1. MVC 概述
    • 2. MVC设计思想
    • 3. 三层架构
    • 4. MVC 与 三层架构的关系:
    • 5. 案例举例:用户账户转账
      • 5.1 M(Model :数据/业务处理层)
      • 5.2 C (Controller : 控制层
      • 5.3 V (View :视图/展示
    • 6. 总结:
    • 7. 最后:

每博一文案

多读书,书中有,你对生活,困难所解不开的答案
比如:《杀死一只是更鸟》中提到的
对应我们:我们努力中考,高考,升本,考研,每天都在努力学习,但是某天突然想到万一没有考上的话,那现在的努力又有什么意义呢?
答案:在《杀死一只是更鸟》里有这样一段话:
> 勇敢是,当你还未开始,你就知道自己会输,可你依然要去做,而且无论如何都要把它坚持到底,你很少能赢,但有时也会。努力的这个过程本身就是有意义,能够获得理想的结果当然很好,但如果失败了也没关系。因为你的勇敢,从未辜负你的青春,而黎明的光亮,总有一刻,会照亮穿梭于黑暗之中的自己。况且,你还不一定会输呢。

1. MVC 概述

MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。比如一批统计数据可以分别用柱状图、饼图来表示。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。 [1-2]

2. MVC设计思想

MVC(Model View Controller)是软件工程中的一种软件架构模式,它把软件系统分为模型视图控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

MVC 主要的核心就是:分层:希望专人干专事,各司其职,职能分工要明确,这样可以让代码耦合度降低,扩展力增强,组件的可复用性增强。

MVC 从字面意思我们就可以看到:是分为了三层的,M(Mode 模型,V(View 视图,C(Controller 控制器

M即model模型:是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。

V即View视图:是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操作的方式。

C即controller控制器:是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

M(Model :数据/业务 V (View :视图/展示 C (Controller : 控制层

C(是核心,是控制器,是司令官

M(处理业务/处理数据的一个秘书)

V(负责页面展示的一个秘书)

MVC(一个司令官,调度两个秘书,去做这件事,仅仅只做事务上的调度,而不做其他的操作

优点:

    耦合性低,方便维护,可以利于分工协作
  1. 重用性高

缺点:

    使得项目架构变得复杂,对开发人员要求高

3. 三层架构

界面层[表示层](User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。

区分层次的目的即为了“高内聚低耦合” 的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。

三层架构的优点

    开发人员可以只关注整个结构中的其中某一层;
  1. 可维护性高,可扩展性高
  2. 可以降低层与层之间的依赖;
  3. 有利于标准化;
  4. 利于各层逻辑的复用

三层架构的缺点:

    降低了系统的性能。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成
  1. 有时会导致级联的修改,这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码
  2. 增加了开发成本

4. MVC 与 三层架构的关系:

MVC的也可以被说成是 MVC三层架构,说白了,它们其实都是一个东西,只是在一些细节上有稍微的不同,大致设计思想都是一样的:“高内聚,低耦合”。

高内聚,低耦合"的思想来设计的。三层中的UI和Servlet来分别对应MVC中的View和Controller,业务逻辑层是来组合数据访问层的原子性功能的。

5. 案例举例:用户账户转账

准备工作:创建表,创建数据



CREATE DATABASE mvc;

USE mvc;

SHOW TABLES;

CREATE TABLE t_act (
   id BIGINT PRIMARY KEY AUTO_INCREMENT,
   actno VARCHAR(255 NOT NULL,
   balance DECIMAL(10,2 
;



INSERT INTO t_act(actno,balance
VALUES('act001',50000.00,('act002',0.00;

SELECT *
FROM t_act;

5.1 M(Model :数据/业务处理层)

javaBean :Account 封装数据

    一般是一张表一个。
  • pojo 对象
  • 有的人也会把这种专门封装数据的对象,称为:"bean对象" (javabean对象,咖啡豆
  • 有的人也会把这种专门封装数据的对象,称为领域模型对象,domain对象
  • 不同的程序员不同的习惯
package com.RainbowSea.bank.mvc;

import java.io.Serializable;
import java.util.Objects;


/**
 * 账户实体类,封装账户信息的
 * 一般是一张表一个。
 * pojo 对象
 * 有的人也会把这种专门封装数据的对象,称为:"bean对象" (javabean对象,咖啡豆
 * 有的人也会把这种专门封装数据的对象,称为领域模型对象,domain对象
 * 不同的程序员不同的习惯。
 */
public class Account implements Serializable {  // 这种普通的简单的对象被成为pojo对象
    // 注意我们这里定义的数据类型,使用引用数据类型
    // 因为我们数据库中可能存在 null 值,而基本数据类型是不可以存储 null值的

    private Long id = null;  // id
    private String actno;  // 账号
    private Double balance; // 余额

    // 反序列化
    private static final long serialVersionUID = 1L;

    public Account( {
    }


    public Account(Long id, String actno, Double balance {
        this.id = id;
        this.actno = actno;
        this.balance = balance;
    }


    public Long getId( {
        return id;
    }

    public void setId(Long id {
        this.id = id;
    }

    public String getActno( {
        return actno;
    }

    public void setActno(String actno {
        this.actno = actno;
    }

    public Double getBalance( {
        return balance;
    }

    public void setBalance(Double balance {
        this.balance = balance;
    }

    @Override
    public boolean equals(Object o {
        if (this == o return true;
        if (!(o instanceof Account return false;
        Account account = (Account o;
        return Objects.equals(getId(, account.getId( && Objects.equals(getActno(, account.getActno( && Objects.equals(getBalance(, account.getBalance(;
    }

    @Override
    public int hashCode( {
        return Objects.hash(getId(, getActno(, getBalance(;
    }

    @Override
    public String toString( {
        return "Account{" +
                "id=" + id +
                ", actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }
}

DB连接数据库的工具:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mvc
user=root
password=MySQL
package com.RainbowSea.bank.utils;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ResourceBundle;

public class DBUtil {

    // resourceBundle 只能读取到 properties 后缀的文件,注意不要加文件后缀名
    private static ResourceBundle resourceBundle = ResourceBundle.getBundle("resources/jdbc";
    private static String driver = resourceBundle.getString("driver";
    private static String url = resourceBundle.getString("url";
    private static String user = resourceBundle.getString("user";
    private static String password = resourceBundle.getString("password";


    // DBUtil 类加载注册驱动
    static {
        try {
            Class.forName(driver;
        } catch (ClassNotFoundException e {
           e.printStackTrace(;
        }

    }


    // 将构造器私有化,不让创建对象,因为工具类中的方法都是静态的,不需要创建对象
    // 为了防止创建对象,故将构造方法私有化
    private DBUtil( {

    }

    /**
     * 这里没有使用数据库连接池,直接创建连接对象
     */
    public static Connection getConnection(  {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(url, user, password;
        } catch (SQLException e {
            throw new RuntimeException(e;
        }
        return  connection;
    }


    /**
     * 资源的关闭
     * 最后使用的最先关闭,逐个关闭,防止存在没有关闭的
     */
    public static void close(Connection connection , PreparedStatement preparedStatement, ResultSet resultSet {

        if (resultSet != null {
            try {
                resultSet.close(;
            } catch (SQLException e {
                throw new RuntimeException(e;
            }
        }


        if (preparedStatement!=null {
            try {
                preparedStatement.close(;
            } catch (SQLException e {
                throw new RuntimeException(e;
            }
        }


        if (connection != null {
            try {
                connection.close(;
            } catch (SQLException e {
                throw new RuntimeException(e;
            }
        }
    }
}

对应Account数据表的DAO操作工具类

什么是DAO ?

    Data Access Object (数据访问对象)
  • DAO实际上是一种设计模式,属于 JavaEE的设计模式之一,不是 23种设计模式
  • DAO只负责数据库表的CRUD ,没有任何业务逻辑在里面
  • 没有任何业务逻辑,只负责表中数据增上改查的对象,有一个特俗的称谓:DAO对象
    这是因为DAO是专门处理t_act 这张表的
  • 如果处理t_act 表的话,可以叫做:UserDao
  • 如果处理t-student表的话,可以叫做 StudentDao

主要定义如下:增删改查方法()

int insert( ;
int deleteByActno(;
int update( ;
Account selectByActno(;
List<Account> selectAll(;
package com.RainbowSea.bank.mvc;


import com.RainbowSea.bank.utils.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

/**
 * AccountDao 是负责Account 数据的增上改查
 * <p>
 * 1. 什么是DAO ?
 * Data Access Object (数据访问对象)
 * 2. DAO实际上是一种设计模式,属于 JavaEE的设计模式之一,不是 23种设计模式
 * 3.DAO只负责数据库表的CRUD ,没有任何业务逻辑在里面
 * 4.没有任何业务逻辑,只负责表中数据增上改查的对象,有一个特俗的称谓:DAO对象
 * 5. 为什么叫做 AccountDao 呢?
 * 这是因为DAO是专门处理t_act 这张表的
 * 如果处理t_act 表的话,可以叫做:UserDao
 * 如果处理t-student表的话,可以叫做 StudentDao
 * <p>
 * int insert( ;
 * int deleteByActno(;
 * int update( ;
 * Account selectByActno(;
 * List<Account> selectAll(;
 */
public class AccountDao {


    /**
     * 插入数据
     *
     * @param account
     * @return
     */
    public int insert(Account account {
        Connection connection = DBUtil.getConnection(;
        PreparedStatement preparedStatement = null;
        int count = 0;
        try {
            String sql = "insert into t_act(actno,balance values(?,?";
            preparedStatement = connection.prepareStatement(sql;
            preparedStatement.setString(1, account.getActno(;
            preparedStatement.setDouble(2, account.getBalance(;
            count = preparedStatement.executeUpdate(;


        } catch (SQLException e {
            throw new RuntimeException(e;
        } finally {
            DBUtil.close(connection, preparedStatement, null;
        }


        return count;

    }


    /**
     * 通过Id删除数据
     *
     * @param id
     * @return
     */
    public int deleteById(String id {
        Connection connection = DBUtil.getConnection(;
        int count = 0;
        PreparedStatement preparedStatement = null;
        try {
            String sql = "delete from t_act where id = ?";
            preparedStatement = connection.prepareStatement(sql;
            preparedStatement.setString(1, id;
            count = preparedStatement.executeUpdate(;
        } catch (SQLException e {
            throw new RuntimeException(e;
        } finally {
            DBUtil.close(connection, preparedStatement, null;
        }

        return count;

    }


    /**
     * 更新数据
     *
     * @param account
     * @return
     */
    public int update(Account account {
        Connection connection = DBUtil.getConnection(;
        PreparedStatement preparedStatement = null;
        int count = 0;

        try {
            String sql = "update t_act set balance = ?, actno = ? where id = ?";
            preparedStatement = connection.prepareStatement(sql;

            //注意设置的 set类型要保持一致。
            preparedStatement.setDouble(1, account.getBalance(;
            preparedStatement.setString(2, account.getActno(;
            preparedStatement.setLong(3, account.getId(;

            count = preparedStatement.executeUpdate(;
        } catch (SQLException e {
            throw new RuntimeException(e;
        } finally {
            DBUtil.close(connection, preparedStatement, null;
        }

        return count;
    }


    /**
     * 通过 actno 查找账户信息
     *
     * @param actno
     * @return
     */
    public Account selectByActno(String actno {
        Connection connection = DBUtil.getConnection(;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        Account account = new Account(;

        try {
            String sql = "select id,actno,balance from t_act where actno = ?";
            preparedStatement = connection.prepareStatement(sql;

            //注意设置的 set类型要保持一致。
            preparedStatement.setString(1, actno;

           resultSet = preparedStatement.executeQuery(;

            if (resultSet.next( {
                Long id = resultSet.getLong("id";
                Double balance = resultSet.getDouble("balance";
                // 将结果集封装到java 对象中
                account.setActno(actno;
                account.setId(id;
                account.setBalance(balance;

            }

        } catch (SQLException e {
            throw new RuntimeException(e;
        } finally {
            DBUtil.close(connection, preparedStatement, resultSet;
        }

        return account;
    }


    /**
     * 查询所有的账户信息
     *
     * @return
     */
    public List<Account> selectAll( {
        Connection connection = DBUtil.getConnection(;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<Account> list = null;

        try {
            String sql = "select id,actno,balance from t_act";
            preparedStatement = connection.prepareStatement(sql;

            resultSet = preparedStatement.executeQuery(;

            while (resultSet.next( {
                String actno = resultSet.getString("actno";
                Long id = resultSet.getLong("id";
                Double balance = resultSet.getDouble("balance";
                // 将结果集封装到java 对象中
                Account account = new Account(id,actno,balance;

                // 添加到List集合当中
                list.add(account;

            }

        } catch (SQLException e {
            throw new RuntimeException(e;
        } finally {
            DBUtil.close(connection, preparedStatement, resultSet;
        }

        return list;
    }


}

对指定的数据表的数据进行service 业务逻辑处理操作:

    AccountService 专门处理Account业务的一个类
  • 在该类中应该编写纯业务代码。(只专注域业务处理,不写别的,不和其他代码混合在一块)
  • 只希望专注业务,能够将业务完美实现,少量bug.
  • 业务类一般起名:XXXService,XXXBiz...
package com.RainbowSea.bank.mvc;


/**
 * service 翻译为:业务。
 * AccountService 专门处理Account业务的一个类
 * 在该类中应该编写纯业务代码。(只专注域业务处理,不写别的,不和其他代码混合在一块)
 * 只希望专注业务,能够将业务完美实现,少量bug.
 * <p>
 * 业务类一般起名:XXXService,XXXBiz...
 */
public class AccountService {

    // 这里的方法起名,一定要体现出,你要处理的是什么业务:
    // 我们要提供一个能够实现转账的业务的方法(一个业务对应一个方法)
    // 比如:UserService StudentService OrderService

    // 处理Account 转账业务的增删改查的Dao
    private AccountDao accountDao = new AccountDao(;

    /**
     * 完成转账的业务逻辑
     *
     * @param fromActno 转出账号
     * @param toActno   转入账号
     * @param money     转账金额
     */
    public void transfer(String fromActno, String toActno, double money throws MoneyNotEnoughException, AppException {
        // 查询余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno;
        if (fromAct.getBalance( < money {
            throw new MoneyNotEnoughException("对不起,余额不足";
        }

        // 程序到这里说明余额充足
        Account toAct = accountDao.selectByActno(toActno;

        // 修改金额,先从内存上修改,再从硬盘上修改
        fromAct.setBalance(fromAct.getBalance( - money;
        toAct.setBalance(toAct.getBalance( + money;


        // 从硬盘数据库上修改
        int count = accountDao.update(fromAct;
        count += accountDao.update(toAct;

        if(count != 2 {
            throw new AppException("账户转账异常,请联系管理员";
        }

    }
}

异常处理类:

package com.RainbowSea.bank.mvc;


/**
 * 余额不足异常
 */
public class AppException extends Exception{

        public AppException( {

        }

        public AppException(String msg {
            super(msg;
        }

}

package com.RainbowSea.bank.mvc;


/**
 * 余额不足异常
 */
public class MoneyNotEnoughException extends Exception{
    public MoneyNotEnoughException( {

    }

    public MoneyNotEnoughException(String msg {
        super(msg;
    }
}

5.2 C (Controller : 控制层

仅仅负责调度 M业务处理层,V视图显示层,而不做其他操作。

package com.RainbowSea.bank.mvc;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;


/**
 * 账户小程序
 * AccountServlet 是一个司令官,他负责调度其他组件来完成任务。
 *
 */
@WebServlet("/transfer"
public class AccountServlet extends HttpServlet { // AccountServlet 作为一个 Controller 司令官

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response throws ServletException,
            IOException {

        // 获取数据
        String fromActno = request.getParameter("fromActno";
        String toActno = request.getParameter("toActno";
        double money = Double.parseDouble(request.getParameter("money";

        // 调用业务方法处理业务(调度Model处理业务,其中是对应数据表的 CRUD操作)
        AccountService accountService = new AccountService(;
        try {
            accountService.transfer(fromActno,toActno,money;
            // 执行到这里说明,成功了,
            // 展示处理结束(调度 View 做页面展示

            response.sendRedirect(request.getContextPath(+"/success.jsp";
        } catch (MoneyNotEnoughException e {
            // 执行到种类,说明失败了,(余额不足
            // 展示处理结束(调度 View 做页面展示)
            response.sendRedirect(request.getContextPath(+"/error.jsp";

        } catch (AppException e {
            // 执行到种类,说明失败了,转账异常
            // 展示处理结束(调度 View 做页面展示)
            response.sendRedirect(request.getContextPath(+"/error.jsp";

        }

        // 页面的展示 (调度View做页面展示)


    }
}

5.3 V (View :视图/展示

index.jsp 转账页面:


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>银行账号转账</title>
</head>
<body>
<form action="<%=request.getContextPath(%>/transfer" method="post">
  转出账户: <input type="text" name="fromActno" /> <br>
  转入账户: <input type="text" name="toActno" /> <br>
  转账金额: <input type="text" name="money" /><br>
  <input type="submit" value="转账" />
</form>
</body>
</html>

success转账成功的页面显示:


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>转账成功</title>
</head>
<body>

<h3>转账成功</h3>
</body>
</html>

error 转账失败的页面显示:


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>转账失败</title>
</head>
<body>
<h3>转账失败</h3>
</body>
</html>

虽然上述:代码成功实现的了用户转账的操作,但是并没有进行事务的处理。

6. 总结:

    MVC 从字面意思我们就可以看到:是分为了三层的,M(Mode 模型,V(View 视图,C(Controller 控制器

  1. M(Model :数据/业务 V (View :视图/展示 C (Controller : 控制层

    C(是核心,是控制器,是司令官

    M(处理业务/处理数据的一个秘书)

    V(负责页面展示的一个秘书)

    MVC(一个司令官,调度两个秘书,去做这件事,仅仅只做事务上的调度,而不做其他的操作

  2. 界面层[表示层](User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。

  3. 高内聚,低耦合"的思想

7. 最后:

感谢如下博主的分享: ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

【2】https://blog.csdn.net/weixin_42694511/article/details/120690083?ops_request_misc

编程笔记 » MVC 三层架构案例详细讲解

赞同 (35) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽