MVC和三层架构总结【动力节点老杜】

简介: 操作数据库的代码和业务逻辑混杂在一起,很容易出错。编写代码的时候很容易出错,无法专注业务逻辑的编写。

MVC【横向】


参考动力节点老杜讲的MVC写的笔记,如有错误,还请指正!


不使用mvc模式实现银行转账功能


分析以下AccountTransferServlet他都负责了什么?


1.负责了数据接收

2.负责了核心的业务处理

3.负责了数据库表中数据的CRUD操作(Create【增】 Retrieve【查】 Update【改】 Delete【删】)

4.负责了页面的数据展示


缺点


  • 代码的复用性太差。(代码的重用性太差)

。因为没有进行“职能分工”,没有独立组件的概念,所以没有办法进行代码复用。代码和代码之间的耦合度太高,扩展力太差。


  • 耦合度高,导致了代码很难扩展。


  • 操作数据库的代码和业务逻辑混杂在一起,很容易出错。编写代码的时候很容易出错,无法专注业务逻辑的编写。


MVC理论基础


  • MVC架构模式【横向】


。M:Model,数据/业务


  • pojo、bean、domain
  • service
  • dao


。V:View,视图/展示

  • JSP
  • Freemarker
  • Velocity
  • Thymeleaf
  • html


。C:Controller,控制器【核心】


35deca5b8fecc05aa01468c2649984f6.png


  • 对mvc的理解?【面试题】


1.mvc是一种软件架构模式


。M指Model,模型


完成具体的业务操作,包括处理业务以及处理数据


。V指View,视图


使用JSP、html完成页面展示


。C指Controller,是核心控制器


负责获取View的请求,并调用模型将数据交给View展示


2.使用mvc架构处理用户请求的具体过程


。当用户发送请求时,控制器接受到用户的请求后,调用Model处理业务


。Model与数据库相连接,处理完业务后将处理结果和处理完的数据返回给控制器


。控制器调用View组件进行结果的展示


3.使用mvc架构的优点


。降低代码的耦合度


。扩展能力增强


。组件的可复用性增强


Model


dao


  • Data Access Object,数据访问对象


  • DAO实际上是一种设计模式,属于JavaEE的设计模式之一(不是23种设计模式)


  • DAO只负责数据库表的CRUD,没有任何业务逻辑在里面


  • 一般情况下,一张表对应一个DAO对象


。比如t_act对应AccoutDao对象


package com.st.bank.dao.impl;
import com.st.bank.dao.AccountDao;
import com.st.bank.pojo.Account;
import com.st.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
 * @author: TIKI
 * @Project: mvc -AccountDaoImpl
 * @Pcakage: com.st.bank.dao.Impl.AccountDaoImpl
 * @Date: 2022年10月23日 19:27
 * @Description:负责account数据的增删改查
 */
public class AccountDaoImpl implements AccountDao {
    /** 插入账户信息
     * @param act 账户信息
     * @return 1表示插入成功
     */
    public int insert(Account act){
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            conn  = DBUtil.getConnection();
            String sql = "insert into t_act(actno,balance) values(?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1,act.getActno());
            ps.setDouble(2,act.getBalance());
            count= ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.close(null,ps,null);
        }
        return count;
    }
    /** 根据主键删除账户
     * @param id 主键
     * @return 删除成功返回1
     */
    public int deleteById(Long id){
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            conn = DBUtil.getConnection();
            String sql = "delete from t_act where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setLong(1,id);
            count = ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.close(null,ps,null);
        }
        return count;
    }
    /** 更新账户
     * @param act
     * @return
     */
    public int update(Account act){
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            conn = DBUtil.getConnection();
            String sql = "update t_act set balance = ?, actno = ? where id=?";
            ps = conn.prepareStatement(sql);
            ps.setDouble(1,act.getBalance());
            ps.setString(2,act.getActno());
            ps.setLong(3,act.getId());
            count = ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.close(null,ps,null);
        }
        return count;
    }
    /** 根据账号查询账户
     * @param actno
     * @return
     */
    public Account selectByActno(String actno){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        Account act = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "select id, balance from t_act where actno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,actno);
            rs = ps.executeQuery();
            if (rs.next()){
                Long id = rs.getLong("id");
                Double balance = rs.getDouble("balance");
                act = new Account(id,actno,balance);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.close(null,ps,rs);
        }
        return act;
    }
    /** 获取所有的账户
     * @return
     */
    public List<Account> selectAll(){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        int count = 0;
        List<Account> list= null;
        try {
            conn = DBUtil.getConnection();
            String sql = "select id, actno,balance from t_act";
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            while (rs.next()){
                Long id = rs.getLong("id");
                String actno =rs.getString("actno");
                Double balance = rs.getDouble("balance");
                list.add(new Account(id,actno,balance));
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            DBUtil.close(null,ps,rs);
        }
        return list;
    }
}


Pojo、bean、domain


  • DAO对象查到的数据如何返回给控制器进而显示到页面?


。利用java语言面向对象的特点->将数据封装成一个对象返回给控制器


  • 比如针对t_act设计Account类【pojo对象】


有的人也会把这种专门封装数据的对象,称为【bean对象】,或者称为领域模型对象【domain对象】


package com.st.bank.pojo;
/**
 * @author: TIKI
 * @Project: mvc -Accout
 * @Pcakage: com.st.bank.mvc.Accout
 * @Date: 2022年10月22日 19:21
 * @Description:账号实体类
 */
public class Account {
    // 一般属性不设计为基本数据类型,建议使用包装类(引用数据类型),防止查询数据为null带来的问题
    private Long id;
    private String actno;
    private Double balance;
    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;
    }
}


。一般类的属性不设计为基本数据类型,建议使用包装类(引用数据类型),防止查询数据为null带来的问题


Service


  • service中编写纯业务代码,service中的方法名需要体现出处理的是什么业务


。比如针对Account业务,编写AccountService业务


package com.st.bank.service.impl;
import com.st.bank.dao.AccountDao;
import com.st.bank.dao.impl.AccountDaoImpl;
import com.st.bank.exceptions.AppException;
import com.st.bank.exceptions.MoneyNotEnoughException;
import com.st.bank.pojo.Account;
import com.st.bank.service.AccountService;
import com.st.bank.utils.DBUtil;
import java.sql.Connection;
import java.sql.SQLException;
/**
 * @author: TIKI
 * @Project: mvc -AccountService
 * @Pcakage: com.st.bank.service.AccountService
 * @Date: 2022年10月23日 19:29
 * @Description: 专门处理Account业务的一个类
 *               在该类中应编写纯业务代码
 */
public class AccountServiceImpl implements AccountService {
    // 为什么定义到这里?因为在每一个业务方法中都可以需要连接数据库。
    private AccountDao accountDao = new AccountDaoImpl();
    // 这里的方法起名,一定要体现出,你要处理的是什么业务。
    // 我们要提供一个能够实现转账的业务方法(一个业务对应一个方法。)
    /**
     * 完成转账的业务逻辑
     * @param fromActno 转出账号
     * @param toActno 转入账号
     * @param money 转账金额
     */
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
        // service层控制事务
        try (Connection connection = DBUtil.getConnection()){
            System.out.println(connection);
            // 开启事务(需要使用Connection对象)
            connection.setAutoCommit(false);
            // 查询余额是否充足
            Account fromAct = accountDao.selectByActno(fromActno);
            if (fromAct.getBalance() < money) {
                throw new MoneyNotEnoughException("对不起,余额不足");
            }
            // 程序到这里说明余额充足
            Account toAct = accountDao.selectByActno(toActno);
            // 修改余额(只是修改了内存中java对象的余额)
            fromAct.setBalance(fromAct.getBalance() - money);
            toAct.setBalance(toAct.getBalance() + money);
            // 更新数据库中的余额
            int count = accountDao.update(fromAct);
            // 模拟异常
            /*String s = null;
            s.toString();*/
            count += accountDao.update(toAct);
            if (count != 2) {
                throw new AppException("账户转账异常!!!");
            }
            // 提交事务
            connection.commit();
        } catch (SQLException e) {
            throw new AppException("账户转账异常!!!");
        }
    }
}


  • 在service中调用dao和view


  • 事务一定是在service中实现的


。一般一个业务方法对应一个完整的事务【不绝对】


事务的解决方法


1.在DAO中方法均传入Connection对象,保证Service中和DAO中使用同一个Connection


2.service调用DAO中的方法时,属于同一个线程,使用Map集合通过线程对象绑定Connection对象

。ThreadLocal:实际上是一下Map集合


简单的ThreadLoacl


package com.powernode.threadlocal;
import java.util.HashMap;
import java.util.Map;
/**
 * 自定义一个ThreadLocal类
 */
public class MyThreadLocal<T> {
    /**
     * 所有需要和当前线程绑定的数据要放到这个容器当中
     */
    private Map<Thread, T> map = new HashMap<>();
    /**
     * 向ThreadLocal中绑定数据
     */
    public void set(T obj){
        map.put(Thread.currentThread(), obj);
    }
    /**
     * 从ThreadLocal中获取数据
     * @return
     */
    public T get(){
        return map.get(Thread.currentThread());
    }
    /**
     * 移除ThreadLocal当中的数据
     */
    public void remove(){
        map.remove(Thread.currentThread());
    }
}


使用ThreadLocal的DButil


package com.st.bank.utils;
import java.sql.*;
import java.util.ResourceBundle;
/**
 * @author: TIKI
 * @Project: mvc -Dbutil
 * @Pcakage: com.st.bank.utils.DBUtil
 * @Date: 2022年10月22日 19:04
 * @Description:数据库工具类
 */
public class DBUtil {
    private static ResourceBundle bundle = ResourceBundle.getBundle("resources/jdbc");
    private static String driver = bundle.getString("driver");
    private static String url = bundle.getString("url");
    private static String user = bundle.getString("user");
    private static String password = bundle.getString("password");
    // 不让创建对象,因为工具类中的方法都是静态的。不需要创建对象。
    // 为了防止创建对象,故将构造方法私有化。
    private DBUtil(){}
    // DBUtil类加载时注册驱动
    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    // 这个对象实际上在服务器中只有一个。
    private static ThreadLocal<Connection> local = new ThreadLocal<>();
    /**
     * 这里没有使用数据库连接池,直接创建连接对象。
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        Connection conn = local.get();
        if (conn == null) {
            conn = DriverManager.getConnection(url, user, password);
            local.set(conn);
        }
        return conn;
    }
    /**
     * 关闭资源
     * @param conn 连接对象
     * @param stmt 数据库操作对象
     * @param rs 结果集对象
     */
    public static void close(Connection conn, Statement stmt, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (conn != null) {
            try {
                conn.close();
                // 思考一下:为什么conn关闭之后,这里要从大Map中移除呢?
                // 根本原因是:Tomcat服务器是支持线程池的。也就是说一个人用过了t1线程,t1线程还有可能被其他用户使用。
                local.remove();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}


  • 思考一下:为什么conn关闭之后,这里要从大Map中移除呢?


根本原因是:Tomcat服务器是支持线程池的。也就是说一个人用过了t1线程,t1线程还有可能被其他用户使用。


三层架构【纵向】


三层架构是指表示层、业务逻辑层和持久化层

  • 表示层/表现层/web层(UI)


直接与前端交互


。接受前端ajax请求


。返回json数据给前端


  • 业务逻辑层Service(BLL)


。处理表现层转发过来的前端请求(具体业务)


。将从持久层获取的数据返回到表现层


  • 持久化层Dao(数据访问层)


直接操作数据库完成CRUD,并将获得的数据返回到上一层,即业务逻辑层


。相关技术


  • JDBC


  • MyBatis


  • Hibernate(实现了JPA规范)


配置较复杂、效率较低


  • JPA


  • SpringData(实现了JPA规范)


  • ActiveJDBC


8805fb86134d1cf7a9b65c90a2aeac36.png


mvc和三层架构之间的关系


e78ca2eb702b006babd319e3f0d58f97.png


  • 三层架构中的表示层即为mvc架构中的View和Controller


  • 三层架构中的业务逻辑层即为mvc架构中Model中的Service


  • 三层架构中的持久化层即为mvc架构中Model中的DAO


SSM与三层架构的关系


  • SSM框架包括Spring、 SpringMVC、MyBatis


。Spring


Spring负责管理整个项目,负责整个项目所有对象的创建、初始化、销毁以及维护对象和对象之间的关系。


  • Spring并不属于三层架构中的任何一层


。SpringMVC(搭建了MVC架构模式)


完成了三层架构中的表示层


  • 作为 View 层的实现者,完成用户的请求接收功能


  • SpringMVC 的 Controller作为整个应用的控制器,完成用户请求的转发及对用户的响应


  • 并提供了业务逻辑层的接口


。MyBatis


作为持久化层的实现者,完成对数据库的增、删、改、查功能


最终实现


不同功能的类放在不同的包下


层和层之间通过接口联系


  • pojo:实体类[M]
  • dao:数据库表(持久化层)[M]

。AccountDao【接口】

。impl

  • AccountDaoImpl【实现类】
  • service:纯业务代码(业务逻辑层)[M]

。AccountService【接口】

。impl

  • AccountServiceImpl【实现类】
  • web:Controller+view(表现层)[V][C]
  • utils:工具类
  • exceptions:异常


0c3a11e35fb9fc3fc3a09e1e41e58a4c.png


待解决


1.service方法中的事务控制代码->通过动态代理机制解决


2.没有完全解决对象和对象之间的依赖关系->使用Spring的IoC容器解决

目录
相关文章
|
7月前
|
设计模式 存储 前端开发
MVVM、MVC、MVP三种常见软件架构设计模式的区别
MVC、MVP 和 MVVM 是三种常见的软件架构设计模式,主要通过分离关注点的方式来组织代码结构,优化开发效率。
154 12
|
2月前
|
存储 前端开发 调度
Flux 与传统的 MVC 架构模式区别
Flux是一种用于构建用户界面的架构模式,与传统的MVC架构不同,它采用单向数据流,通过Dispatcher统一管理数据的分发,Store负责存储数据和业务逻辑,View只负责展示数据,使得应用状态更加可预测和易于维护。
|
5月前
|
设计模式 前端开发 数据库
哇塞!Rails 的 MVC 架构也太牛了吧!快来看看这令人惊叹的编程魔法,开启新世界大门!
【8月更文挑战第31天】《Rails中的MVC架构解析》介绍了Ruby on Rails框架核心的MVC设计模式,通过模型(Model)、视图(View)和控制器(Controller)三部分分离应用逻辑,利用Active Record进行数据库操作,ERB模板渲染视图,以及控制器处理用户请求与业务逻辑,使代码更易维护和扩展,提升团队开发效率。
88 0
|
8月前
|
安全 数据管理 中间件
云LIS系统源码JavaScript+B/S架构MVC+SQLSugar医院版检验科云LIS系统源码 可提供演示
检验科云LIS系统源码是医疗机构信息化发展的重要趋势。通过云计算技术实现数据的集中管理和共享可以提高数据利用效率和安全性;通过高效灵活的系统设计和可扩展性可以满足不同医疗机构的需求;通过移动性和智能化可以提高医疗服务的精准度和效率;通过集成性可以实现医疗服务的协同性和效率。因此,多医院版检验科云LIS系统源码将成为未来医疗机构信息化发展的重要方向之一。
103 2
|
4月前
|
设计模式 前端开发 数据库
理解mvc架构
mvc架构
43 4
|
5月前
|
设计模式 存储 前端开发
MVC革命:如何用一个设计模式重塑你的应用架构,让代码重构变得戏剧性地简单!
【8月更文挑战第22天】自定义MVC(Model-View-Controller)设计模式将应用分为模型、视图和控制器三个核心组件,实现关注点分离,提升代码可维护性和扩展性。模型管理数据和业务逻辑,视图负责数据显示与用户交互,控制器处理用户输入并协调模型与视图。通过示例代码展示了基本的MVC框架实现,可根据需求扩展定制。MVC模式灵活性强,支持单元测试与多人协作,但需注意避免控制器过度复杂化。
52 1
|
6月前
|
存储 前端开发 算法
MVC(Model-View-Controller)架构
MVC架构帮助开发者构建清晰、可维护和可扩展的Web应用程序。
52 2
|
7月前
|
设计模式 前端开发 Java
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
130 1
|
8月前
|
存储 监控 关系型数据库
关系型数据库设计集群架构节点规划
【5月更文挑战第6天】在实际项目中,可能还需要考虑其他因素,如安全性、合规性、成本等。因此,在进行关系型数据库设计集群架构节点规划时,建议与经验丰富的数据库管理员和架构师合作,以确保项目的成功实施和稳定运行。
74 4
关系型数据库设计集群架构节点规划
|
7月前
|
存储 前端开发 数据库
MVC模式和三层架构
MVC模式和三层架构
66 2

热门文章

最新文章