JSP-05三层架构+MVC

8 篇文章 0 订阅
3 篇文章 0 订阅

一、回顾三层架构

1.1基本概念

三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:

1、界面层(User Interface layer)JSP、HTML5

2、业务逻辑层(Business Logic Layer)

3、数据访问层(Data access layer)持久层(DAO)


1.2作用与组成

区分层次的目的即为了 “高内聚低耦合” 的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层。


1.3三层架构命名规范

这里以第三波书店为案例

1.3.1UI层(表示层)

在创建JavaWeb项目的时候我们的项目中会包含一个名称为webappweb的目录,在该目录下的xxx.jspxxx.html页面就是所谓的表示层,通俗点来说就是我们客户端浏览器运行应用程序时所看到的操作界面。


1.3.2业务逻辑层

该层主要提供业务服务接口和接口对应的实现类

例如service包下的BookService接口

package com.bookshop.service;

import com.bookshop.entity.Book;

import java.util.List;

/**
 * 业务逻辑层,图书业务接口
 *
 * @author Aiden
 */
public interface BookService {
    //按照指定的条数查询图书列表
    List<Book> findListByCount(Integer count);

    //分页查询图书信息
    List<Book> findListByPage(Integer pageIndex, Integer pageSize, Integer categoryId);

    //根据图书分类查询图书总条数
    Long getTotalCount(Integer categoryId);

    //根据图书主键编号查询图书信息
    Book findByBookId(String bookId);

}

例如service.impl包下的BookServiceImpl

package com.bookshop.service.impl;

import com.bookshop.dao.BookDao;
import com.bookshop.dao.impl.BookDaoImp;
import com.bookshop.entity.Book;
import com.bookshop.service.BookService;
import com.bookshop.utils.JDBCUtils;

import java.util.List;

/**
 * 图书业务实现类
 *
 * @author Aiden
 */
public class BookServiceImpl implements BookService {
    //创建dao实例
    private BookDao dao = new BookDaoImp();

    @Override
    public List<Book> findListByCount(Integer count) {
        List<Book> list = null;
        try {
            list = dao.findListByCount(count);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return list;
    }

    @Override
    public List<Book> findListByPage(Integer pageIndex, Integer pageSize, Integer categoryId) {
        List<Book> list = null;
        try {
            Integer currentPage = (pageIndex - 1) * pageSize;
            list = dao.findListByPage(currentPage, pageSize, categoryId);
        } catch (Exception exception) {
            exception.printStackTrace();
        } finally {
            JDBCUtils.close();
        }
        return list;
    }

    @Override
    public Long getTotalCount(Integer categoryId) {
        Long count = 0L;
        try {
            count = dao.getTotalCount(categoryId);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return count;
    }

    @Override
    public Book findByBookId(String bookId) {
        Book book = null;
        try {
            book = dao.findByBookId(bookId);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return book;
    }
}

1.3.3数据访问层

该层主要提供数据服务接口和接口对应的实现类

例如dao包下的BookDao接口

package com.bookshop.dao;

import com.bookshop.entity.Book;

import java.util.List;

/**
 * 数据访问层,图书DAO接口
 *
 * @author Aiden
 */
public interface BookDao {
    //按照指定的条数查询图书列表
    List<Book> findListByCount(Integer count) throws Exception;

    //分页查询图书信息
    List<Book> findListByPage(Integer pageIndex, Integer pageSize, Integer categoryId) throws Exception;

    //根据图书类型查询总条数
    Long getTotalCount(Integer categoryId) throws Exception;

    //新增图书信息
    int insert(Book book) throws Exception;

    //修改图书信息
    int update(Book book) throws Exception;

    //删除图书信息
    int delete(Integer bookId) throws Exception;

    //修改上下架状态(sale:0:下 1:上架)
    int updateSaleByBookId(Integer bookId, Integer sale) throws Exception;

    //根据图书主键编号查询图书信息
    Book findByBookId(String bookId) throws Exception;
}

例如dao.impl包下的BookDaoImpl接口

package com.bookshop.dao.impl;

import com.bookshop.dao.BookDao;
import com.bookshop.entity.Book;
import com.bookshop.utils.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

/**
 * 图书DAO接口实现类,提供对MySql数据库服务器的CURD操作
 * @author Aiden
 */
public class BookDaoImp implements BookDao {
    
    QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());

    @Override
    public List<Book> findListByCount(Integer count) throws Exception {
        Connection connection = JDBCUtils.getConnection();
        String sql = "SELECT * from Books LIMIT ?";
        List<Book> list = queryRunner.query(connection, sql, new BeanListHandler<>(Book.class), count);
        //关闭连接对象
        JDBCUtils.closeConnection(connection);
        return list;
    }

    @Override
    public List<Book> findListByPage(Integer pageIndex, Integer pageSize, Integer categoryId) throws Exception {
        Connection connection = JDBCUtils.getConnection();
        StringBuffer sql = new StringBuffer();
        List<Object> params = new ArrayList<>();
        sql.append("select * from books ");
        if (categoryId != null && categoryId > 0) {
            sql.append("where categoryId=? ");
            params.add(categoryId);
        }
        sql.append(" limit ?,? ");
        params.add(pageIndex);
        params.add(pageSize);
        List<Book> list = queryRunner.query(connection, sql.toString(), new BeanListHandler<>(Book.class), params.toArray());
        //关闭连接对象
        JDBCUtils.closeConnection(connection);
        return list;
    }

    @Override
    public Long getTotalCount(Integer categoryId) throws Exception {
        Connection connection = JDBCUtils.getConnection();
        StringBuffer sql = new StringBuffer();
        List<Object> params = new ArrayList<>();
        sql.append("select count(0) from books");
        if (categoryId != null && categoryId > 0) {
            sql.append(" where categoryId=? ");
            params.add(categoryId);
        }
        Long totalCount = (Long) queryRunner.query(connection, sql.toString(), new ScalarHandler<>(1), params.toArray());
        //关闭连接对象
        JDBCUtils.closeConnection(connection);
        return totalCount;
    }
        @Override
    public Book findByBookId(String bookId) throws Exception {
        String sql = "select b.*,c.categoryName,p.publisherName from books b \n" +
                "inner join category c on b.categoryId=c.categoryId \n" +
                "inner join publisher p on b.publisherId=p.publisherId\n" +
                "where b.bookId=?";
        //获取连接
        Connection connection = JDBCUtils.getConnection();
        Book book = queryRunner.query(sql, new BeanHandler<>(Book.class), bookId);
        //关闭连接
        JDBCUtils.closeConnection(connection);
        return book;
    }

    @Override
    public int insert(Book book) throws Exception {
        return 0;
    }

    @Override
    public int update(Book book) throws Exception {
        return 0;
    }

    @Override
    public int delete(Integer bookId) throws Exception {
        return 0;
    }

    @Override
    public int updateSaleByBookId(Integer bookId, Integer sale) throws Exception {
        return 0;
    }

}

二、MVC

2.1MVC简介

MVC 全名是Model View Controller,是 模型(model)视图(view)控制器(controller) 的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

模型:

模型是应用程序的主体部分,表示业务数据和业务逻辑。一个模型能为多个视图提供数据。由于应用于模型的代码只需要写一次就可以被多个视图重用,所以提高了代码的可重用性。

视图

视图是用户看到并与之交互的界面,可以向用户显示相关的数据,也可以接收用户的输入,但是不进行任何实际的业务处理。

控制器

控制器主要用于接收客户端请求,获取请求参数,调用业务层服务方法,决定用哪个模型组件去处理请求,然后决定调用哪个视图来显示模型处理返回的数据。


2.2MVC与三层架构的关系

M :(Model) 对应三层架构中的 JavaBean (数据访问层+业务逻辑层)

V :(View) 对应三层架构中的表示层(JSP页面/html页面

C :(Controller)控制器(ServletJSP

在这里插入图片描述


2.3在Web应用中使用MVC架构原因

用户界面逻辑的更改往往比业务逻辑频繁,尤其是在基于Web的应用程序中。

例如,可能添加新的用户界面页,或者可能完全打乱现有的页面布局。对显示的更改,尽可能地不要影响到数据和业务逻辑。

目前大部分Web应用都是将数据代码和表示混在一起。经验比较丰富的开发者会将数据从表示层分离开来,但这通常不是很容易做到的,它需要精心的计划和不断的尝试。MVC从根本上强制性的将它们分开。尽管构造MVC应用需要一些额外的工作,但它带来的好处是无庸质疑的。


2.4MVC的优点

(1) 有利于团队开发分工协作和质量控制,降低开发成本。

(2) 可以为一个模型在运行时同时建立和使用多个视图。变化-传播机制可以确保所有相关的视图及时得到模型数据变化,从而使所有关联的视图和控制器做到行为同步。

(3) 视图与控制器的可接插性,允许更换视图和控制器对象,而且可以根据需求动态的打开或关闭、甚至在运行期间进行对象替换。

(4) 模型的可移植性。因为模型是独立于视图的,所以可以把一个模型独立地移植到新的平台工作。需要做的只是在新平台上对视图和控制器进行新的修改。

(5) 潜在的框架结构。可以基于此模型建立应用程序框架,不仅仅是用在设计界面的设计中。


2.5MVC的缺点

(1)增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。

(2)视图对模型数据的访问效率低。视图可能需要多次调用Model才能获得足够的显示数据。

(3)完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。同时由于模型和视图要严格的分离,这样也给调试应用程序到来了一定的困难。


三、三层架构和MVC的区别

MVC(模型Model-视图View-控制器Controller)是一种架构模式,可以用它来创建在域对象和UI表示层对象之间的区分。

同样是架构级别的,相同的地方在于他们都有一个表现层,但是他们不同的地方在于其他的两个层。

在三层架构中没有定义Controller的概念。这是最不同的地方。而MVC也没有把业务的逻辑访问看成两个层,这是采用三层架构或MVC搭建程序最主要的区别。当然了。在三层中也提到了Model,但是三层架构中Model的概念与MVC中Model的概念是不一样的,“三层”中典型的Model层是由业务逻辑与访问数据组成的。而MVC里,则是以实体类构成的。


四、总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyqyrR0b-1657336177070)(E:\BDQN\BCSP\课件笔记\JSP\jsp 04 MVC与三层架构.assets\1657335013720.png)]


五、补充

关于上文业务实现类与dao实现类中出现的JDBCUtils为数据库操作工具辅助类,该类的实现代码如下:

maven依赖

<!--mysql数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>
<!--druid数据源-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.10</version>
</dependency>
<!--数据库操作工具类-->
<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.7</version>
</dependency>
<!--servlet-api-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>

JDBCUtils工具类

package com.bookshop.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * JDBCUtils工具类
 */
public class JDBCUtils {
    private static DruidDataSource dataSource;//数据源
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();//本地线程
    private static Connection conn;//连接对象

    //静态代码块
    static {
        InputStream is = null;
        try {
            Properties properties = new Properties();
            is = JDBCUtilPlus.class.getClassLoader().getResourceAsStream("druid.properties");
            properties.load(is);
            is.close();
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();//关闭流
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取连接
     * @return
     */
    public static Connection getConnection() {
        try {
            conn = threadLocal.get();
            if (conn == null) {
                conn = dataSource.getConnection();
                threadLocal.set(conn);
            }
            return conn;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取数据源
     * @return
     */
    public static DataSource getDataSource() {
        return dataSource;
    }

    /**
     * 开启事务
     */
    public static void startTransaction() {
        try {
            Connection connection = getConnection();
            if (connection != null) {
                connection.setAutoCommit(false);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    public static void commitTransaction() {
        try {
            Connection connection = threadLocal.get();
            if (connection != null) {
                connection.commit();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    public static void rollbackTransaction() {
        try {
            Connection connection = threadLocal.get();
            if (connection != null) {
                connection.rollback();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭事务
     */
    public static void close() {
        try {
            Connection connection = threadLocal.get();
            if (connection != null) {
                if (!connection.getAutoCommit()) {
                    threadLocal.remove();
                    connection.close();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 普通关闭
     *
     * @param connection
     */
    public static void closeConnection(Connection connection) {
        try {
            if (connection != null) {
                if (connection.getAutoCommit()) {
                    threadLocal.remove();
                    connection.close();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


druid.properties配置类

#数据库连接基础信息
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/bookshopplus?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root
password=root
#初始化连接数个数
initialSize=20
#最大连接数个数
maxActive=100
#空闲的连接数个数
minIdle=6
#获取连接最大等待时间
maxWait=60000
#从连接池获取连接时,是否检测连接可用性,开启性能会有些许影响
testOnBorrow=true
#启用空闲连接检测,以便回收
testWhileIdle=true
#释放连接到连接池时,是否检测连接可用性,开启性能会有些许影响
testOnReturn=false
#系统启动时通过该sql语句验证数据库是否可用,例如oracle用SELECT 'x' from dual,mysql用SELECT 'x'
validationQuery=select 'x'
#监控数据库
filters=start
#关闭超过30分钟的空闲连接,1800秒,也就是30分钟
removeAbandonedTimeout=1800
#对于长时间不使用的连接强制关闭
removeAbandoned=true
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis=60000

Servlet控制器

package com.bookshop.servlet;

import com.bookshop.entity.Book;
import com.bookshop.service.BookService;
import com.bookshop.service.impl.BookServiceImpl;
import com.bookshop.utils.Page;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * Servlet 图书管理控制器
 * @author Aiden
 */
@WebServlet("/BookServlet")
public class BookServlet extends HttpServlet {
    //用于处理客户端请求服务方法
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //查询、新增、修改、删除
        String action = request.getParameter("action");
        action = (action == null) ? "select" : action;
        BookService bookService = new BookServiceImpl();
        switch (action) {
            case "select": //查询图书
                selectBookInfo(request, response, bookService);
                break;
            case "details"://查询图书详情信息
                queryBookDetails(request, response, bookService);
                break;
            case "insert": //新增操作
                break;
            case "update": //修改操作
                break;
            case "delete": //删除操作
                break;
            default:
                selectBookInfo(request, response, bookService);
                break;
        }
    }

    /**
     * 查询图书详情信息
     */
    private void queryBookDetails(HttpServletRequest request, HttpServletResponse response, BookService bookService) throws ServletException, IOException {
        //根据图书主键编号,查询图书对象,传递到details.jsp页面
        String bookId = request.getParameter("bookId");
        Book book = bookService.findByBookId(bookId);
        request.setAttribute("book", book);
        request.getRequestDispatcher("details.jsp").forward(request, response);
    }

    /**
     * 查询图书信息
     */
    private void selectBookInfo(HttpServletRequest request, HttpServletResponse response, BookService bookService) throws ServletException, IOException {
        //获取前端请求的页码索引
        Integer pageIndex = request.getParameter("pageIndex") == null ? 1 : Integer.parseInt(request.getParameter("pageIndex"));
        Integer pageSize = 12;
        //获取图书分类的编号
        Integer categoryId = request.getParameter("cId") == null ? 0 : Integer.parseInt(request.getParameter("cId"));
        //查询总条数
        Long totalCount = bookService.getTotalCount(categoryId);
        //分页数据列表
        List<Book> bookList = bookService.findListByPage(pageIndex, pageSize, categoryId);

        Page<Book> page = new Page<Book>(pageIndex, pageSize, totalCount.intValue(), bookList);
        //将数据保存在request作用域中
        request.setAttribute("page", page);
        request.setAttribute("cId", categoryId);
        //跳转到图书首页
        request.getRequestDispatcher("booklist.jsp").forward(request, response);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

众生云海,一念初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值