• 谈一谈我对三层架构和MVC模式的理解


    初识MVC模式和三层架构

    前言

    本文主要是博主对于这两者之间的个人认知,由于博主的认知能力有限,很可能存在纰漏,所以如有表述不当,或理解有误,恳请大家及时指出\~ o( ̄▽ ̄)ブ,让我们一起加油(ง •_•)ง,共同进步吧💪

    前置知识

    首先我们需要了解什么是设计模式框架架构模式架构。这些知识,更加有利于我们对MVC模式和三层架构的理解(●’◡’●)

    • 设计模式(Design pattern),又称软件设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。本质就是对某一问题的一套最优解决方案

      主要作用提高代码的复用性

    • 框架(Framework)是构成一类特定软件可复用设计的一组相互协作的类。本质是半本成品软件

      主要作用提高开发效率

    • 架构模式,也叫架构风格,用于描述软件系统里的基本的结构组织或纲要。本质是一种具有指导意义的思想

      主要作用规范软件的开发

    • 架构,又称软件架构(software architecture),是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。本质是软件或系统的设计大纲

      主要作用提高软件的稳定性、可维护性和安全性

    它们之间的联系

    设计模式是一种针对某个具体的问题而做出的解决方案,而框架的主要目的是为了实现代码的重用,在实现期间很可能使用到多种设计模式;架构模式是一种指导思想,根据这种指导思想工程师可以创造出基于这种思想的多种框架;而架构是一个项目的大纲,这个大纲用于软件的设计,在设计这个软件期间可以会用到多种架构模式。

    举个简略的例子加以印证:当我们在设计一款软件时,必须先由架构师做好架构,规划好软件的整体结构;然后确定需要使用哪些架构模式,比如要解决软件系统的数据与显示分离的问题,那就使用MVC模式……;最后交由下面的程序员进行开发,程序员开发过程中可能会使用多种框架,比如Spring、SpringMVC、MyBatis……;同时这些框架的实现很可能使用了多种设计模式,比如MyBatis就使用了单例模式、工厂模式、代理模式……

    它们之间可以理解是一个层层包含关系,如图所示:

    架构
    架构模式
    框架
    设计模式

    什么是MVC模式?

    MVC(Model View Controller)模式是一种架构模式,主要思想是M和V的分离,它要求软件开发时,需要进行将【业务的处理】和【视图显示】 的实现代码进行分离,然后使用控制器对他们进行调度。

    • MVC 是一种分层开发的模式,其中:

      • M(Model):业务模型,主要用来处理业务逻辑,负责对数据库进行CRUD的基本操作,然后由这些基本操作构成一套业务处理方法,比如:查询、删除、修改等功能
      • V(View):视图窗口,主要用来进行页面展示,负责接收来自控制器的数据,并将其响应到浏览器的网页上

      • C(Controller):控制器,主要用来调度M和V,根据用户请求来选择要调用哪个模型来处理业务,以及最终由哪个视图为用户做出应答

    image-20220808152249524

    控制器(serlvlet)用来接收浏览器发送过来的请求,控制器调用模型(JavaBean)来获取数据,比如从数据库查询数据;控制器获取到数据后再交由视图(JSP)进行数据展示

    • MVC的优点
      • 降低了系统的复杂度。该模式符合面对对象设计原则之一的单一职责原则(也称康威定律),能够有效降低系统复杂度
      • 提高了开发效率。每一层之间都分工明确,职责单一,有利于开发人员的分工协作
      • 有利于组件重用。能够随时更换某一层的代码,对其他层的影响很小

    什么是三层架构?

    三层架构是一种架构模式,主要思想是“高内聚,低耦合”思想,它将各个功能模块划分为表现层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构。

    • 三层架构是将我们的项目分成了三个层面,分别:
      • 表现层(UI,User Interface):主要用来展示数据。负责和浏览器打交道,接收、请求、封装来自浏览器的请求数据,同时调用业务逻辑层的方法,然后将来自业务逻辑层的数据进行封装,最后将封装的数据响应到网页上
      • 业务逻辑层(BLL,Bussiness Logic Level):主要用来处理业务逻辑。负责调用数据访问层的SQL语句,实现业务功能,比如注册、登录功能的实现,同时获取来并封装自数据访问层的数据
      • 数据访问层(DAL,Data Access Layer):主要用来进行数据访问。负责和数据库打交道,对数据库进行CRUD基本操作

    image-20220808155130257

    • 三层架构的优点
      • 提高代码的复用性。因为每个模块都是遵循”高内聚,低耦合“的设计思想,很容易抽取出来进行复用
      • 实现各层的解耦。有利于项目的维护、更新
      • 安全性高。用户端只能通过业务逻辑层来调用数据访问层,减少了入口点,把很多危险的系统功能都屏蔽了

    MVC和三层架构的关系

    两者的区别

    • 从适用范围上看

      • 三层架构是一个分层式的软件体系架构设计,适用于许多的项目。
      • MVC模式是为了让前端和业务逻辑代码和数据分开,只适用在web项目中。
    • 从目的上看

      两者的主要目的都是为了解耦,从而提高项目的可维护性,扩展性。三层架构侧重的是项目整体的解耦,而MVC侧重的是前端页面和业务逻辑处理的一个解耦,较之而言,三层架构解耦更彻底。

    • 从层次上看

      • 三层架构是框架层面上的
      • 而MVC模式是设计模式层面上的

      一个软件肯定要先确定好框架,之后才有下一步的设计模式,所以三层架构的层次要高于MVC模式

    其次:三层架构的分层具有上下关系,上层依赖于下层。但MVC作为表现模式是不存在上下关系的,而是相互协作关系。

    两者的关系

    虽然MVC和三层架构都是架构模式,本质上都是一种软件设计上的指导思想,但两者在抽象层面和适用范围上还有有很大的区别的。三层架构是方法论,只提供一种抽象的指导思想,告诉开发者设计软件需要进行分层开发;而MVC提供了一个相对具体的指导思想,告诉开发者如何进行分层。可以理解MVC是三层架构这个方法论中的一种方法,他们是包含关系。

    image-20220808155433260


    案例:

    一般而言,我们都会将三层架构和MVC搭配使用,整体使用三层架构,对于表现层使用VC模式。

    任务:利用三层架构+MVC模式实现对tb_brand表的增、删、改、查功能

    备注:本案例已经开源到Gitee和Github,感兴趣的小伙伴可以参观:

    • 知识汲取者的Github仓库:✈️传送门
    • 知识汲取者的Gitee仓库:✈️传送门
    前期准备:
    创建Web项目
    创建目录
    导入依赖
    连接数据库
    创建表
    编写MyBatis配置文件
    编写实体类
    • Step1:使用Maven创建Web项目

      详细步骤请参考:一文教你快速上手Maven

    • Step2:创建项目所需的目录

      image-20220810070739247

      三层架构:

      • 数据访问层:mapper
      • 业务逻辑层:service、工具(pojo、utils)
      • 表现层:JSP、HTML、web

      MVC:

      • 模型:mapper、service、工具(pojo、utils)
      • 视图:JSP、HTML
      • 控制器:web
    • Step3:导入项目所需依赖

      项目除了下面依赖的Tomcat7插件外,还需要依赖MavenHelper、MyBatisX两款插件。

      其实这些插件也不是必须品,只是为了方便开发😄

      pom.xml:

      
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          
          
          <modelVersion>4.0.0modelVersion>
          
          <groupId>com.hhxygroupId>
          <artifactId>day9_brand-demoartifactId>
          <version>1.0-SNAPSHOTversion>
          
          <packaging>warpackaging>
          
          <properties>
              <maven.compiler.source>16maven.compiler.source>
              <maven.compiler.target>16maven.compiler.target>
          properties>
          
      
          
          <dependencies>
              
              <dependency>
                  <groupId>mysqlgroupId>
                  <artifactId>mysql-connector-javaartifactId>
                  <version>8.0.27version>
              dependency>
              
              <dependency>
                  <groupId>org.mybatisgroupId>
                  <artifactId>mybatisartifactId>
                  <version>3.5.5version>
              dependency>
              
              <dependency>
                  <groupId>javax.servletgroupId>
                  <artifactId>javax.servlet-apiartifactId>
                  <version>3.1.0version>
                  <scope>providedscope>
              dependency>
              
              <dependency>
                  <groupId>javax.servlet.jspgroupId>
                  <artifactId>jsp-apiartifactId>
                  <version>2.2version>
                  
                  <scope>providedscope>
              dependency>
              
              <dependency>
                  <groupId>jstlgroupId>
                  <artifactId>jstlartifactId>
                  <version>1.2version>
              dependency>
              <dependency>
                  <groupId>taglibsgroupId>
                  <artifactId>standardartifactId>
                  <version>1.1.2version>
              dependency>
          dependencies>
      
          
          <build>
              <plugins>
                  
                  <plugin>
                      <groupId>org.apache.tomcat.mavengroupId>
                      <artifactId>tomcat7-maven-pluginartifactId>
                      <version>2.2version>
                      <configuration>
                          <port>8080port>
                          
                      configuration>
                  plugin>
              plugins>
          build>
          
          
      project>
      
    • Step4:使用IDEA连接数据库servlet(没有可以创建一个)

      详细步骤请参考:手把手教你快速入门MyBatis从此解放你的双手

    • Step5:在IDEA中建立一张tb_brand表

      tb_brand:

      -- 删除tb_brand表
      drop table if exists tb_brand;
      -- 创建tb_brand表
      create table tb_brand
      (
          -- id 主键
          id           int primary key auto_increment,
          -- 品牌名称
          brand_name   varchar(20),
          -- 企业名称
          company_name varchar(20),
          -- 排序字段
          ordered      int,
          -- 描述信息
          description  varchar(100),
          -- 状态:0:禁用  1:启用
          status       int
      );
      -- 添加数据
      insert into tb_brand (brand_name, company_name, ordered, description, status)
      values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
             ('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
             ('小米', '小米科技有限公司', 50, 'are you ok', 1);
      
      
      SELECT * FROM tb_brand;
      

      这一步既可以在IDEA中进行,又可以在Navicat中进行(推荐在IDEA中进行)

    • Step6:编写MyBatis配置文件

      1)MyBatis核心配置文件:

      mybatis-config.xml:

      
      DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
          
          <typeAliases>
              <package name="com.hhxy.pojo"/>
          typeAliases>
          
          <environments default="development">
              <environment id="development">
                  <transactionManager type="JDBC"/>
                  <dataSource type="POOLED">
                      
                      <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql:///servlet?useSSL=false&characterEncoding=utf8&useServerPrepStmts=true"/>
                      <property name="username" value="root"/>
                      <property name="password" value="32345678"/>
                  dataSource>
              environment>
          environments>
          
          <mappers>
              
              
              <package name="com.hhxy.mapper"/>
          mappers>
      
      configuration>
      

      2)SQL映射文件

      BrandMapper.xml:

      
      DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <mapper namespace="com.hhxy.mapper.BrandMapper">
          
          <resultMap id="brandResultMap" type="Brand">
              <result column="brand_name" property="brandName">result>
              <result column="company_name" property="companyName">result>
          resultMap>
          
          
          <update id="update">
              update tb_brand
              <set>
                  <if test="brandName!=null and brandName!=''">
                      brand_name = #{brandName},
                  if>
                  <if test="companyName!=null and companyName!=''">
                      company_name = #{companyName},
                  if>
                  <if test="ordered!=null and ordered!=''">
                      ordered = #{ordered},
                  if>
                  <if test="description!=null and description!=''">
                      description = #{description},
                  if>
                  <if test="status != null">
                      status = #{status}
                  if>
              set>
              where id = #{id};
          update>
          
          <delete id="deleteById">
              delete from tb_brand where id=#{id};
          delete>
      mapper>
      
    • Step7:编写pojo实体类

      Brand:

      package com.hhxy.pojo;
      /**
       * 数据访问层(DAL)& 业务模型(Model)
       * 数据访问层:
       */
      public class Brand {
          // id 主键
          private Integer id;
          // 品牌名称
          private String brandName;
          // 企业名称
          private String companyName;
          // 排序字段
          private Integer ordered;
          // 描述信息
          private String description;
          // 状态:0:禁用  1:启用
          private Integer status;
      
      
          public Brand() {
          }
      
          public Brand(Integer id, String brandName, String companyName, String description) {
              this.id = id;
              this.brandName = brandName;
              this.companyName = companyName;
              this.description = description;
          }
      
          public Brand(Integer id, String brandName, String companyName, Integer ordered, String description, Integer status) {
              this.id = id;
              this.brandName = brandName;
              this.companyName = companyName;
              this.ordered = ordered;
              this.description = description;
              this.status = status;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getBrandName() {
              return brandName;
          }
      
          public void setBrandName(String brandName) {
              this.brandName = brandName;
          }
      
          public String getCompanyName() {
              return companyName;
          }
      
          public void setCompanyName(String companyName) {
              this.companyName = companyName;
          }
      
          public Integer getOrdered() {
              return ordered;
          }
      
          public void setOrdered(Integer ordered) {
              this.ordered = ordered;
          }
      
          public String getDescription() {
              return description;
          }
      
          public void setDescription(String description) {
              this.description = description;
          }
      
          public Integer getStatus() {
              return status;
          }
      
          public void setStatus(Integer status) {
              this.status = status;
          }
      
          @Override
          public String toString() {
              return "Brand{" +
                      "id=" + id +
                      ", brandName='" + brandName + '\'' +
                      ", companyName='" + companyName + '\'' +
                      ", ordered=" + ordered +
                      ", description='" + description + '\'' +
                      ", status=" + status +
                      '}';
          }
      }
      
    正式开始:

    这里可以顺序不固定,可以根据个人习惯而言。我比较习惯自下而上从数据访问层到表现层来编写(前提要先明确功能和业务逻辑)实际工作中,推荐自上而下去编写,这样当我们需要什么时就可以加什么

    编写Mapper接口
    编写SQL映射文件
    编写Service类
    编写Servlet类
    编写html
    编写jsp
    编写web.xml
    编写工具类
    • Step1:编写Mapper接口

      BrandMapper:

      package com.hhxy.mapper;
      
      import com.hhxy.pojo.Brand;
      import org.apache.ibatis.annotations.Insert;
      import org.apache.ibatis.annotations.ResultMap;
      import org.apache.ibatis.annotations.Select;
      
      import java.util.List;
      /**
       * 数据访问层(DAL)& 业务模型(Model)
       * 数据访问层:对数据库的CRUD
       * 业务模型:既要进行CRUD,又要进行逻辑功能的实现
       *           这里是业务模型的一部分,对数据库进行CRUD的基本操作
       */
      public interface BrandMapper {
          /*查询所有功能*/
          @Select("select * from tb_brand")
          @ResultMap("brandResultMap")
          List<Brand> selectAll();
      
          /*添加功能*/
          @Insert("insert into tb_brand(brand_name,company_name,ordered,description,status) " +
                  "values(#{brandName},#{companyName},#{ordered},#{description},#{status})")
          void add(Brand brand);
      
          /*修改功能*/
          //更具id查询数据
          @Select("select * from tb_brand where id=#{id}")
          @ResultMap("brandResultMap")
          Brand selectById(int id);
          //根据id进行数据修改
          void update(Brand brand);
      
          /*删除功能*/
          void deleteById(int id);
      
      }
      
    • Step2:编写SQL映射文件

      BrandMapper.xml:

      
      DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <mapper namespace="com.hhxy.mapper.BrandMapper">
          
          <resultMap id="brandResultMap" type="Brand">
              <result column="brand_name" property="brandName">result>
              <result column="company_name" property="companyName">result>
          resultMap>
          
          
          <update id="update">
              update tb_brand
              <set>
                  <if test="brandName!=null and brandName!=''">
                      brand_name = #{brandName},
                  if>
                  <if test="companyName!=null and companyName!=''">
                      company_name = #{companyName},
                  if>
                  <if test="ordered!=null and ordered!=''">
                      ordered = #{ordered},
                  if>
                  <if test="description!=null and description!=''">
                      description = #{description},
                  if>
                  <if test="status != null">
                      status = #{status}
                  if>
              set>
              where id = #{id};
          update>
          
          <delete id="deleteById">
              delete from tb_brand where id=#{id};
          delete>
      
      mapper>
      
    • Step3:编写工具类

      SqlSessionFactoryUtils:

      package com.hhxy.utils;
      
      import org.apache.ibatis.io.Resources;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.apache.ibatis.session.SqlSessionFactoryBuilder;
      
      import java.io.IOException;
      import java.io.InputStream;
      
      /**
       * 获取工厂对象的工具类
       * 利用了设计模式中的单例模式,单例模式中的饿汉单例模式
       */
      
      public class SqlSessionFactoryUtils {
          //1、定义成员变量,用于获取局部变量的值
          private static SqlSessionFactory sqlSF;
          //2、在静态代码块中获取SqlSessionFactory对象
          static{
              //2.1 定位MyBatis核心配置文件
              String resource = "./mybatis-config.xml";
              try {
                  //2.2 加载MyBatis核心配置文件,获取输入流对象
                  InputStream is = Resources.getResourceAsStream(resource);
                  //2.3 获取SqlSessionFactory对象
                  sqlSF = new SqlSessionFactoryBuilder().build(is);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      
          /**
           * get方法,用于外部获取SqlSessionFactory对象
           * @return
           */
          public static SqlSessionFactory getSqlSF() {
              return sqlSF;
          }
      }
      
    • Step4:编写Service

      BrandService:

      package com.hhxy.service;
      
      import com.hhxy.mapper.BrandMapper;
      import com.hhxy.pojo.Brand;
      import com.hhxy.utils.SqlSessionFactoryUtils;
      import org.apache.ibatis.session.SqlSession;
      import org.apache.ibatis.session.SqlSessionFactory;
      
      import java.util.List;
      
      /**
       * 业务逻辑层(BLL)& 业务模型(Model)
       * 业务逻辑层:调用数据访问层的基础功能,进行逻辑处理,实现业务功能
       * 业务模型:包括了业务逻辑层 和 数据访问层的所有功能
       *           这里是业务模型的一部分,进行逻辑处理,实现业务功能
       *
       */
      public class BrandService {
          //因为所有的方法都需要获取sqlSF对象,所以将其由局部变量变为成员,提升它的作用域
          SqlSessionFactory sqlSF = SqlSessionFactoryUtils.getSqlSF();
      
          /*查询所有功能*/
          /**
           * 查询所有数据
           * @return
           */
          public List<Brand> selectAll(){
              //1、使用工具类获取SqlSessionFactory对象
      
              //2、获取SqlSession对象
              SqlSession sqlS = sqlSF.openSession(true);
              //3、获取Mapper接口对象
              BrandMapper mapper = sqlS.getMapper(BrandMapper.class);
              //4、执行SQl语句
              List<Brand> brands = mapper.selectAll();
              //5、提交事物(已设置为自动提交)
              //6、释放资源
              sqlS.close();
              //7、返回查询结果
              return brands;
          }
      
          /*新增功能*/
          /**
           * 添加数据
           * @param brand
           */
          public void add(Brand brand){
              //1、使用工具类获取SqlSessionFactory对象
              //2、获取SqlSession对象
              SqlSession sqlS = sqlSF.openSession(true);
              //3、获取Mapper接口对象
              BrandMapper mapper = sqlS.getMapper(BrandMapper.class);
              //4、执行SQL语句
              mapper.add(brand);
              //5、提交事物(已设置为自动提交)
              //6、释放资源
              sqlS.close();
          }
      
          /*修改功能*/
          /**
           * 根据Id查询数据
           */
          public Brand selectById(int id){
              //1、使用工具类获取SqlSessionFactory对象
              //2、获取SqlSession对象
              SqlSession sqlS = sqlSF.openSession(true);
              //3、获取Mapper接口对象
              BrandMapper mapper = sqlS.getMapper(BrandMapper.class);
              //4、执行SQL语句
              Brand brand = mapper.selectById(id);
              //5、提交事物(已自动提交)
              //6、释放资源
              sqlS.close();
              //7、返回查询结果
              return brand;
          }
          /**
           * 更新数据
           */
          public void update(Brand brand){
              //1、使用工具类获取SqlSessionFactory对象
              //2、获取SqlSession对象
              SqlSession sqlS = sqlSF.openSession(true);
              //3、获取Mapper接口对象
              BrandMapper mapper = sqlS.getMapper(BrandMapper.class);
              //4、执行SQL语句
              mapper.update(brand);
              //5、提交事物(已手动提交)
              //6、释放资源
              sqlS.close();
          }
      
          /*删除功能*/
          /**
           * 更具id删除数据
           */
          public void delete(int id){
              //1、使用工具类获取SQL Session Factory对象
              //2、获取SqlSession对象
              SqlSession sqlS = sqlSF.openSession(true);
              //3、获取Mapper接口对象
              BrandMapper mapper = sqlS.getMapper(BrandMapper.class);
              //4、执行SQl语句
              mapper.deleteById(id);
              //5、提交事务(已手动提交)
              //6、释放资源
              sqlS.close();
          }
      
      }
      
    • Step5:编写Servlet

      1)SelectAllServlet:

      package com.hhxy.web;
      
      import com.hhxy.pojo.Brand;
      import com.hhxy.service.BrandService;
      
      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.ArrayList;
      import java.util.List;
      
      /**
       * 结构划分:表现层(UI)& 控制器( Controller)
       * 表现层:包括了控制器 和 视图的功能
       *         这里是表现层的一部分,调用业务逻辑层(Service)的功能获取数据并转发数据(然后展示数据)
       * 控制器:调用业务模型(Model)的功能获取数据并转发数据(展示数据交给视图)
       */
      @WebServlet("/SelectAllServlet")
      public class SelectAllServlet extends HttpServlet {
          //细节:因为在该类中要多次使用BrandService对象所以提升作用域
          BrandService service = new BrandService();
      
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              System.out.println("doPost方法被调用了");
      
              this.doGet(request, response);
          }
      
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              System.out.println("doGet方法被调用了");
              /*查询所有*/
              //1、获取BrandService对象
              //2、调用BrandService对象完成查询
              List<Brand> brands = service.selectAll();
              //3、将数据存入Request域对象中
              request.setAttribute("brands",brands);
              //4、请求转发到brand.js,让其响应数据
              request.getRequestDispatcher("/jsp/brand.jsp").forward(request,response);
          }
      }
      
      

      2)AddServlet:

      package com.hhxy.web;
      
      import com.hhxy.pojo.Brand;
      import com.hhxy.service.BrandService;
      
      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.nio.charset.StandardCharsets;
      
      @WebServlet("/AddServlet")
      public class AddServlet extends HttpServlet {
          //细节:因为在该类中要多次使用BrandService对象所以提升作用域
          BrandService service = new BrandService();
      
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doGet(request, response);
          }
      
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              request.setCharacterEncoding("utf-8");
              //1、获取BrandService对象
              //2、接收请求参数(用户输入的数据)
              String brandName = request.getParameter("brandName");
              String companyName = request.getParameter("companyName");
              String ordered = request.getParameter("ordered");
              String description = request.getParameter("description");
              String status = request.getParameter("status");
      
              //3、解决中文乱码
              //方案一:Post方式:
              //request.setCharacterEncoding("utf-8");//这行代码要放在2、的前面
              /*
              //方案二:通用方式:
              //Step1:设置编码
              byte[] bytes1 = brandName.getBytes(StandardCharsets.ISO_8859_1);
              byte[] bytes2 = companyName.getBytes(StandardCharsets.ISO_8859_1);
              byte[] bytes3 = description.getBytes(StandardCharsets.ISO_8859_1);
              //Step2:设计解码
              brandName = new String(bytes1,StandardCharsets.UTF_8);
              companyName = new String(bytes2,StandardCharsets.UTF_8);
              description = new String(bytes3,StandardCharsets.UTF_8);
              */
      
              //4、封装数据
              Brand brand = new Brand();
              brand.setBrandName(brandName);
              brand.setCompanyName(companyName);
              brand.setOrdered(Integer.parseInt(ordered));
              brand.setDescription(description);
              brand.setStatus(Integer.parseInt(status));
              //5、调用addService对象完成添加
              service.add(brand);
              //6、请求转发到brand.js,查看添加后的结果
              request.getRequestDispatcher("/SelectAllServlet").forward(request,response);
      
          }
      }
      
      

      3)SelectByIdServlet:

      package com.hhxy.web;
      
      import com.hhxy.pojo.Brand;
      import com.hhxy.service.BrandService;
      
      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;
      
      @WebServlet("/SelectByIdServlet")
      public class SelectByIdServlet extends HttpServlet {
          //细节:因为在该类中要多次使用BrandService对象所以提升作用域
          BrandService service = new BrandService();
      
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doGet(request, response);
          }
      
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              /*回显数据*/
              //1、获取BrandService对象
              //2、接收请求参数
              String id = request.getParameter("id");
              //3、BrandService对象完成完成查询
              Brand brand = service.selectById(Integer.parseInt(id));
              //4、将数据存储到Request域中
              request.setAttribute("brand",brand);
              //5、请求转发到updateBrand.jsp中,让其响应数据
              request.getRequestDispatcher("/jsp/updateBrand.jsp").forward(request,response);
          }
      }
      
      

      4)UpdateServlet:

      package com.hhxy.web;
      
      import com.hhxy.pojo.Brand;
      import com.hhxy.service.BrandService;
      
      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.nio.charset.StandardCharsets;
      
      @WebServlet("/UpdateServlet")
      public class UpdateServlet extends HttpServlet {
          //细节:因为在该类中要多次使用BrandService对象,所以将其作用域提升
          BrandService service = new BrandService();
      
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doGet(request, response);
          }
      
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      //        request.setCharacterEncoding("utf-8");
              //1、获取Service对象
              //2、接收请求参数
              String id = request.getParameter("id");
              String brandName = request.getParameter("brandName");
              String companyName = request.getParameter("companyName");
              String ordered = request.getParameter("ordered");
              String description = request.getParameter("description");
              String status = request.getParameter("status");
      
              //3、解决中文乱码
              //方案一:Post方式
              //request.setCharacterEncoding("utf-8");//这行代码一定要放在获取请求参数之前!!!
              //方案二:通用方式:
              //Step1:设置编码
              byte[] bytes1 = brandName.getBytes(StandardCharsets.ISO_8859_1);
              byte[] bytes2 = companyName.getBytes(StandardCharsets.ISO_8859_1);
              byte[] bytes3 = description.getBytes(StandardCharsets.ISO_8859_1);
              //Step2:设计解码
              brandName = new String(bytes1,StandardCharsets.UTF_8);
              companyName = new String(bytes2,StandardCharsets.UTF_8);
              description = new String(bytes3,StandardCharsets.UTF_8);
      
              //4、封装数据
              Brand brand = new Brand();
              brand.setId(Integer.parseInt(id));
              brand.setBrandName(brandName);
              brand.setCompanyName(companyName);
              brand.setOrdered(Integer.parseInt(ordered));
              brand.setDescription(description);
              brand.setStatus(Integer.parseInt(status));
      
              //5、调用Service对象完成更新
              service.update(brand);
      
              //6、请求转发到brand.js,查看更新后的结果
              request.getRequestDispatcher("/SelectAllServlet").forward(request,response);
      
          }
      }
      

      5)DeleteServlet:

      package com.hhxy.web;
      
      import com.hhxy.pojo.Brand;
      import com.hhxy.service.BrandService;
      
      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;
      
      @WebServlet("/DeleteServlet")
      public class DeleteServlet extends HttpServlet {
          //细节:因为在该类中要多次使用BrandService对象所以提升作用域
          BrandService service = new BrandService();
      
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doGet(request, response);
          }
      
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              //1、获取BrandService对象
              //2、接收请求参数
              String id = request.getParameter("id");
              //3、BrandService对象完成完成查询
              service.delete(Integer.parseInt(id));
              //4、请求转发到updateBrand.jsp中,让其响应数据
              request.getRequestDispatcher("/SelectAllServlet").forward(request,response);
          }
      }
      
    • Step6:编写HTML

      DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Titletitle>
      head>
      <body>
      <a href="http://localhost:8080/day9_brand-demo/SelectAllServlet">点击查看a>
      body>
      html>
      
    • Step7:编写JSP

      1)brand.jsp:

      <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%--
          结构划分:表现层(UI)& 视图(View)
          表现层:包括了视图 和 控制器的功能
                  这里是表现层的一部分,用于展示数据
           视图:展示数据
      --%>
      
      
          
          展示所有数据
      
      
      

      <%--========================核心代码============================--%> <%--==========================================================--%>
      序号 品牌名称 企业名称 排序 品牌介绍 状态 操作
      ${brand.id} ${brand.brandName} ${brand.companyName} ${brand.ordered} ${brand.description} ${brand.status == 1 ? "启用":"禁用"} 修改 删除
      <%--增加点击事件--%>

      2)addBrand.jsp:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      
      
      
      
          
          添加数据
      
      
      

      添加品牌

      品牌名称:
      企业名称:
      排序:
      描述信息:
      状态: 禁用 启用

      3)updateBrand.jsp:

      <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%--<%@page isELIgnored="false" %>--%>
      
          
          修改数据
      
      
      

      添加品牌

      <%--隐藏域,提交id,让UpdateServlet确定更新数据时的id--%> 品牌名称:
      企业名称:
      排序:
      描述信息:
      状态: 禁用 启用
      禁用 启用
    • Step8:编写web.xml

      
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
               version="4.0">
          
          <welcome-file-list>
              <welcome-file>html/index.htmlwelcome-file>
          welcome-file-list>
      web-app>
      
    效果演示:

    image-20220810080440644

    image-20220810080450479

    image-20220810080508327

    image-20220810080559512

    案例分析:

    由于这些代码,业务逻辑大致都差不多,所以这里就拿相比之下较难的修改功能的实现逻辑来进行分析。

    image-20220810095740522

    遇到过的问题:

    • 不熟练的地方:

      • 忘记导入Servlet依赖和JSP依赖没有加provided

      • 忘记设置打包方式war

      • 忘记将依赖导全,导致500

    • 粗心的地方:

      • 依赖导错,导致500
      • 跳转路径配置错误,导致404
      • req.setCharacterEncoding("UTF-8");代码放在获取请求参数后面,导致乱码
    • 值得注意的地方:

      • 重定向需要加虚拟目录的,请求转发不需虚拟目录,其中html页面跳转要写全url
      • 请求数据中文乱码(这个打算单独写一篇文章总结以下我遇到过的乱码问题)
      • 用户输入的限制交给前端干,我们不需要去管接收的数据是否会不合理(所以测试要使用正确数据)

    还有很多小错误,就不一 一列举了,主要原因还是不熟练😫……


    PS:

    现在对这两个东西有了一点大致的认知了,这东西还得实践才能更好地理解,看终究是难以理解的(纸上得来终觉浅,绝知此事要躬行),真正的理解还得靠后期逐渐的学习啊,如果觉得本文对你有所帮助,欢迎点赞👍+评论✍加油!少年,路还长😄


    参考文章

  • 相关阅读:
    JVM垃圾收集器总结
    实现一个博客系统(前端页面设计)
    搞定预设,让你的 ChatGPT 不受限制
    RuntimeError:Input, output and indices must be on the current device
    医院各领域榜单。22个科室、100种常见疾病
    leetcode 46. 全排列(经典)
    8.11学习日志 后缀数组+DLX
    020—pandas 根据历史高考分段推断当前位次的分数
    【深度学习】小结1-入门两周学习感受
    2023/09/17
  • 原文地址:https://blog.csdn.net/qq_66345100/article/details/127112623