Servlet之Request和Response的快速上手

阅读提示:

1、Request和Response概述

  • request:获取请求数据

    • 浏览器会发送HTTP请求到后台服务器[Tomcat]
    • HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
    • 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
    • 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
    • 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
  • response:设置响应数据

    • 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
    • 把响应数据封装到response对象中
    • 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
    • 浏览器最终解析结果,把内容展示在浏览器给用户浏览
  • 案例

    @WebServlet("/demo")
    public class ServletDemo extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //使用request对象 获取请求数据
            String name = request.getParameter("name"); //用于获取请求参数,如url?name=zhangsan
    
            //使用response对象 设置响应数据
            response.setHeader("content-type","text/html;charset=utf-8");
            response.getWriter().write("<h1>"+name+",欢迎您!</h1>");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("Post...");
        }
    }
    

2、Request对象

2.1 Request继承体系

类或接口 说明
ServletRequest Java提供的请求对象根接口
--
HttpServletRequest Java提供的针对HTTP协议封装的请求对象接口
--
RequestFacade Tomcat定义的实现类

2.2 Request获取请求数据

2.2.1 获取请求行

  • 请求行示例

    GET /request-demo/req1?name=zhangsan&password=123/ HTTP/2

    分别为请求方式、请求资源路径和参数、协议和版本号

  • 获取方法

    // 获取请求方式 GET
    String getMethod();
    // 获取虚拟目录(项目访问路径) /request-demo
    String getContextPath();
    // 获取URL(统一资源定位符)http://localhost:8080/request-demo/req1
    StringBuffer getRequestURL();
    // 获取URI(统一资源标识符)/request-demo/req1
    String getRequestURI();
    // 获取请求参数(GET方式)name=zhangsan&password=123
    String getQueryString();
    

2.2.2 获取请求头

  • 请求头示例

    User-Agent: Mozilla/5.0 Chrome/91.0.4472.106

  • 获取方法

    // 获取请求头,参数为对应的键
    String getHadder(String name);
    

2.2.3 获取请求体

  • 请求体用于POST请求方式

  • 获取方法

    // 获取字节流,用于二进制数据
    ServletInputStream getInputStream();
    // 获取字符流,用于纯文本数据
    BufferedReader getReader();
    

2.2.4 获取请求参数的通用方式

  • 存在问题

    BufferedReader getReader();获取文本数据需要手动解析

  • 改进方法

    // 获取所有参数Map集合(Map集合的Value元素采用数据,以应对多选等情况)
    Map<String,String[]> getParameterMap();
    // 根据名称获取参数值(数组)
    String[] getParameterValues(String name);
    // 根据名称获取参数值(单个值)
    String getParameter(String name);
    

2.3 请求参数中文乱码问题

2.3.1 POST请求解决方案

  • 分析原因

    • POST的请求参数是通过request的getReader()来获取流中的数据

    • TOMCAT在获取流的时候采用的编码是ISO-8859-1

  • 在获取参数前设置使用需要的编码

    // 设置字符输入流编码,设置字符集和页面保持一致,不区分大小写
    request.setCharacterEncoding("UTF-8");
    

2.3.2 GET请求解决方案

  • 分析原因

    1. 字符串用过HTTP由浏览器提交到服务器
    2. 服务器不支持中文,会将字符进行URL编码
    3. 将URL编码发送到服务器,由服务器进行解码
    4. TOMCAT服务器对GET请求获取请求参数的方式是request.getQueryString(),与POST请求不同
    5. TOMCAT默认使用ISO-8859-1进行URL解码
    6. TOMCAT中getParameter(String name);方法将URL解码采用的编码格式写死,无法修改
  • 在获取参数前设置使用需要的编码

    // 浏览器采用UTF-8将字符进行RUL编码
    // 服务器采用ISO-8859-1进行URL解码,此时采用编码为ISO-8859-1,出现乱码
    
    // 使用ISO-8859-1编码将乱码字符编回二进制码,得到其字节数组 
    byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
    // 将字节数组使用UTF-8重新编码
    username = new String(bytes, StandardCharsets.UTF_8);
    
  • 备注

    Tomcat 8.0以上

    解决了GET请求的乱码问题,设置了URL解码的默认方式为UTF-8

  • URL编解码

    // 编码
    URLEncoder.encode(str,"UTF-8");
    // 解码
    URLDecoder.decode(s,"ISO-8859-1");
    

2.4 Request 请求转发

  • 请求转发是一种在服务器内部的跳转方式

  • 实现请求转发

    1. 接收资源A请求,执行其对应Servlet方法
    2. 资源A中要跳转到资源B
    3. 将请求转发到资源B
    4. 执行资源BServlet方法
    // 在资源A中
    req.getRequestDispatcher("资源B路径").forward(req,resp);
    
  • 请求转发资源间共享数据:使用Request对象

    将该需要共享的数据(有需要时封装为对象)放在Request域中,可在转发后进行获取

    // 以(k, v)存储数据到request域中
    void setAttribute(String name,Object o);
    // 根据key获取value
    Object getAttribute(String name);
    // 根据key删除该键值对
    void removeAttribute(String name);
    
  • 请求转发的特点

    • 浏览器地址栏的路径不发生变化
    • 只能转发到当前服务器内部资源
    • 一次请求,可以在转发的资源间使用request共享数据

3、Response对象

3.1 Response继承体系

类或接口 说明
ServletResponse Java提供的响应对象根接口
--
HttpServletResponse Java提供的针对HTTP协议封装的响应对象
--
ResponseFacade Tomcat定义的实现类

3.2 Response设置响应数据功能介绍

  • 设置响应行

    HTTP/2 200 OK

    // 设置响应状态码
    void setStatus(int sc);
    
  • 设置响应头

    Content-Type:text/html

    // 设置响应头键值对
    void setHeader(String name,String value);
    
  • 设置响应体

    HTML代码等,通过字符、字节输出流的方式网浏览器写

    // 获取字符输出流
    PrintWriter getWriter();
    // 获取字节输出流
    ServletOutputStream getOutputStream();
    

3.3 Response请求重定向

  • 重定向:一种资源跳转的方式,区别于转发

    1. 浏览器发送发送请求到服务器,其中资源A接收到请求
    2. 资源A无法处理,但知道资源B资源可以处理,对浏览器做出响应,告知状态和处理方法
      • 响应状态码:302
      • 响应头给出资源位置:location:xxx
    3. 浏览器接收到响应状态码302
    4. 浏览器重新发送请求到请求头中location对应的地址寻找到资源B
    5. 资源B接收请求后,进行处理,返回结果到服务器
  • 重定向实现方式

    /*
    // 设置响应状态码和响应头
    resp.setStatus(302);
    resp.setHeader("location","项目虚拟目录(项目访问路径)+资源B的访问路径");
    */
    
    // 设置重定向
    resp.sendRedirect("虚拟目录(项目访问路径)+资源B的访问路径");
    
  • 重定向的特点(区别于请求转发)

    • 浏览器地址栏路径发生变化
    • 可以重定向到任意资源位置,不局限于服务器内部
    • 两次请求之间,不能在多个资源使用Request域共享数据

3.4 路径问题

  • 关于虚拟目录(明确路径被谁使用)

    • 浏览器使用:需要加虚拟目录(项目访问路径) -- 如重定向
    • 服务器使用:无需虚拟目录 -- 如请求转发
  • 举例

    • 需要加虚拟目录
      • <a herf='路径'>
      • <from action='路径'></from>
      • resp.sendRedirect("路径")
    • 不需要虚拟目录
      • req.getRequestDispatcher("路径");
  • 动态获取虚拟目录

    <!-- pom.xml中全局配置 -->
    <build>
    	<plugins>
        	<plugin>
            	<groupId>org.apache.toncat.maven</groupId>
                <artifactId>tomcat-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                	<path> <!-- 全局虚拟目录 --> </path>
                </configuration>
            </plugin>
            <!-- ... -->
        </plugins>
    </build>
    
    // 动态获取虚拟目录
    String contextPath = request.getContextPath();
    

3.5 Response相应字符数据

字符输出流不需要关闭,其由Response对象获取,会随Response对象的回收而回收

  • 返回简单字符串aaa

    // 通过Response对象获取字符输出流
    PrintWriter writer = response.getWriter();
    // 写数据
    writer.write("aaa");
    
  • 返回HTML代码,切能被解析

    // 通过Response对象获取字符输出流
    PrintWriter writer = response.getWriter();
    // 设置响应头content-type,让浏览器以HTML格式进行解析
    response.setHeader("content-type","text/html");
    // 写HTML代码
    writer.write("<h1>aaa</h1>");
    
  • 返回中文字符串,设置编码格式为UTF-8(通过Response获取的字符输出流默认编码为ISO-8859-1

    // 通过Response对象获取字符输出流
    PrintWriter writer = response.getWriter();
    // 设置响应头content-yupe,使用HTML格式解析,并注明字符集
    // response.setHeader("content-type","text/html;cahrset=utf-8");
    response.setContentType("text/html;cahrset=utf-8");
    // 写HTML代码
    writer.write("你好");
    // writer.write("<h1>你好</h1>");
    

3.6 Response相应字节数据

  • 返回字节数据使用方法

    // 通过Response对象获取字节输出流
    ServletOutputStream outputStream = resp.getOutputStream();
    // 通过字节输出流写数据
    outputStream.write(字节数据);
    
  • 示例

    // 读取文件
    FileInputStream fis = new FileInputStream("d://demo.jpg");
    // 获取response字节输出流
    ServletOutputStream os = response.getOutputStream();
    // 完成流的copy
    byte[] buff = new byte[1024];
    int len = 0;
    while ((len = fis.read(buff))!= -1){
        os.write(buff, 0, len);
    }
    
    fis.close();
    
  • 使用工具类改进示例

    <!-- 在pom.xml中添加配置 -->
    <dependencies>
        <!-- ... -->
        <dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.6</version>
            </dependency>
        </dependency>
    </dependencies>
    
    // 读取文件
    FileInputStream fis = new FileInputStream("d://demo.jpg");
    // 获取response字节输出流
    ServletOutputStream os = response.getOutputStream();
    // 完成流的copy
    // byte[] buff = new byte[1024];
    // int len = 0;
    // while ((len = fis.read(buff))!= -1){
    //     os.write(buff, 0, len);
    // }
    IOUtils.copy(fis, os);
    
    fis.close();
    

4、用户注册登录案例

4.1用户登录和注册案例

4.1.1 需求分析

  • 登录
    1. 用户填写用户名密码,提交到LoginServlet
    2. 在LoginServlet中使用MyBatis查询数据库,验证用户名密码是否正确
    3. 如果正确响应登录成功,否则响应登录失败
  • 注册
    1. 用户填写用户名密码,提交到RegisterServlet
    2. 在LoginServlet中使用MyBatis查询数据库,验证用户名是否存在
    3. 若不存在则向数据库中插入新的用户数据,否则响应注册失败

4.1.2 环境准备

  • 依赖导入

    <?xml version="1.0" encoding="UTF-8"?>
    
    <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.0</modelVersion>
    
      <groupId>priv.dandelion</groupId>
      <artifactId>day09RequestResponse</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>
    
      <dependencies>
        <!--mybatis 依赖-->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.5</version>
        </dependency>
    
        <!--mysql 驱动-->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.34</version>
        </dependency>
    
        <!--junit 单元测试-->
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
        </dependency>
    
        <!-- 添加slf4j日志api -->
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>1.7.20</version>
        </dependency>
    
        <!-- 添加logback-classic依赖 -->
        <dependency>
          <groupId>ch.qos.logback</groupId>
          <artifactId>logback-classic</artifactId>
          <version>1.2.3</version>
        </dependency>
    
        <!-- 添加logback-core依赖 -->
        <dependency>
          <groupId>ch.qos.logback</groupId>
          <artifactId>logback-core</artifactId>
          <version>1.2.3</version>
        </dependency>
    
        <!-- servlet依赖 -->
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
    
        <!-- io工具类依赖 -->
        <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>2.6</version>
        </dependency>
    
      </dependencies>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
    <!--        <configuration>-->
    <!--&lt;!&ndash;          <path>&lt;!&ndash; 全局虚拟目录 &ndash;&gt;</path>&ndash;&gt;-->
    <!--          <path>/day09RequestResponse</path>-->
    <!--        </configuration>-->
          </plugin>
        </plugins>
      </build>
    </project>
    
    
  • 页面

    注:代码中虚拟目录位置需要进行替换

    • 登录

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
          <meta charset="UTF-8">
          <title>login</title>
          <link href="css/login.css" rel="stylesheet">
      </head>
      
      <body>
      <div id="loginDiv">
          <form action="/虚拟目录/login" method="post" id="form">
              <h1 id="loginMsg">LOGIN IN</h1>
              <p>Username:<input id="username" name="username" type="text"></p>
      
              <p>Password:<input id="password" name="password" type="password"></p>
      
              <div id="subDiv">
                  <input type="submit" class="button" value="login up">
                  <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
                  <a href="register.html">没有账号?点击注册</a>
              </div>
          </form>
      </div>
      
      </body>
      </html>
      
    • 注册

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>欢迎注册</title>
          <link href="css/register.css" rel="stylesheet">
      </head>
      <body>
      
      <div class="form-div">
          <div class="reg-content">
              <h1>欢迎注册</h1>
              <span>已有帐号?</span> <a href="login.html">登录</a>
          </div>
          <form id="reg-form" action="/虚拟目录/register" method="post">
      
              <table>
      
                  <tr>
                      <td>用户名</td>
                      <td class="inputs">
                          <input name="username" type="text" id="username">
                          <br>
                          <span id="username_err" class="err_msg" style="display: none">用户名不太受欢迎</span>
                      </td>
      
                  </tr>
      
                  <tr>
                      <td>密码</td>
                      <td class="inputs">
                          <input name="password" type="password" id="password">
                          <br>
                          <span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
                      </td>
                  </tr>
      
      
              </table>
      
              <div class="buttons">
                  <input value="注 册" type="submit" id="reg_btn">
              </div>
              <br class="clear">
          </form>
      
      </div>
      </body>
      </html>
      
  • 数据库和实体类

    • User表

      -- 创建用户表
      CREATE TABLE tb_user(
      	id int primary key auto_increment,
      	username varchar(20) unique,
      	password varchar(32)
      );
      
      -- 添加数据
      INSERT INTO tb_user(username,password) values('zhangsan','123'),('lisi','234');
      
      SELECT * FROM tb_user;
      
      
    • 实体类

      package priv.dandelion.entity;
      
      public class User {
          private Integer id;
          private String username;
          private String password;
      
          public User(Integer id, String username, String password) {
              this.id = id;
              this.username = username;
              this.password = password;
          }
      
          public User() {
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getUsername() {
              return username;
          }
      
          public void setUsername(String username) {
              this.username = username;
          }
      
          public String getPassword() {
              return password;
          }
      
          public void setPassword(String password) {
              this.password = password;
          }
      
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", username='" + username + '\'' +
                      ", password='" + password + '\'' +
                      '}';
          }
      }
      
      
  • MyBatis

    • 核心配置文件

      注:代码中数据库名称需要进行替换

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
          <environments default="development">
              <environment id="development">
                  <!-- 采用JDBC的事务管理方式 -->
                  <transactionManager type="JDBC"/>
                  <!-- 数据库连接信息 -->
                  <dataSource type="POOLED">
                      <property name="driver" value="com.mysql.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql:///数据库名称?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="123456"/>
                  </dataSource>
              </environment>
      
              <environment id="test">
                  <transactionManager type="JDBC"/>
                  <!-- 数据库连接信息 -->
                  <dataSource type="POOLED">
                      <property name="driver" value="com.mysql.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql:///数据库名称?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="123456"/>
                  </dataSource>
              </environment>
          </environments>
          <!-- 加载SQL映射文件 -->
          <mappers>
              <package name="priv.dandelion.mapper"/>
          </mappers>
      </configuration>
      
    • UserMapper.xml映射文件

      先完成接口,再由MyBatisX插件生成,或手动再resources目录下创建

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
      <mapper namespace="priv.dandelion.mapper.UserMapper">
      </mapper>
      
    • UserMapper接口

      public interface UserMapper {
      
          // 查询已存在用户数量,防止重复注册
          @Select("select * from tb_user where username = #{username} limit 0, 1")
          public User selectByUsername(@Param("username") String username);
      
          // 查询用户信息
          @Select("select * from tb_user where username = #{username} and password = #{password} limit 0, 1")
          public User selectAll(@Param("username") String username, @Param("password") String password);
      
          @Insert("insert into tb_user(username, password) VALUES (#{username}, #{password})")
          public void insertUser(@Param("username") String username, @Param("password") String password);
      }
      
      

4.1.3 代码实现

注:实现流程

  1. 接收用户名和密码
  2. 解决Request中文乱码
    • 使用ISO-8859-1编码将乱码字符编回二进制码,得到其字节数组
    • 将字节数组使用UTF-8重新编码
  3. 调用MyBatis完成查询
    • 加载MyBatis核心配置文件,获取SqlSessionFactory
    • 获取Session对象,设置自动提交事务
    • 获取UserMapper
    • 执行SQL
  4. 处理结果并返回数据
    • 设置response编码并获取字符输出流
    • 处理结果(注册时需要额外判断用户是否已经存在)
  5. 释放资源
  • 登录Servlet

    @WebServlet("/login")
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            System.out.println("开始进行登录判断");
    
            // 接收用户名和密码
            String username = request.getParameter("username");
            String password = request.getParameter("password");
    
            // 解决Request中文乱码
            // 使用ISO-8859-1编码将乱码字符编回二进制码,得到其字节数组
            byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
            // 将字节数组使用UTF-8重新编码
            username = new String(bytes, StandardCharsets.UTF_8);
    
            // 调用MyBatis完成查询
            // 加载MyBatis核心配置文件,获取SqlSessionFactory
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            // 获取Session对象,设置自动提交事务
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            // 获取UserMapper
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 执行SQL
            User user = userMapper.selectAll(username, password);
    
            // 设置response编码并获取字符输出流
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
    
            // 处理结果
            if (user != null) {
                // 登录成功
                writer.write("login success" + user.getUsername() + "欢迎您");
            } else {
                // 登录失败
                writer.write("login fail 用户" + user.getUsername() + "不存在");
            }
            // 释放资源
            sqlSession.close();
            System.out.println("login end");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
  • 注册Servlet

    @WebServlet("/register")
    public class RegisterServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("register start");
            // 接收用户名和密码
            String username = request.getParameter("username");
            String password = request.getParameter("password");
    
            // 解决Request中文乱码
            // 使用ISO-8859-1编码将乱码字符编回二进制码,得到其字节数组
            byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
            // 将字节数组使用UTF-8重新编码
            username = new String(bytes, StandardCharsets.UTF_8);
    
            // 调用MyBatis完成查询
            // 加载MyBatis核心配置文件,获取SqlSessionFactory
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            // 获取Session对象,设置自动提交事务
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            // 获取UserMapper
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 执行SQL,处理结果
            User user = userMapper.selectByUsername(username);
    
            // 设置response编码并获取字符输出流
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter writer = response.getWriter();
    
            // 查重
            if (user == null){
                userMapper.insertUser(username, password);
                // 注册成功
                writer.write("register success 成功");
            } else {
                System.out.println(user.getUsername());
                // 注册失败
                writer.write("用户名已被占用");
            }
    
            // 释放资源
            sqlSession.close();
            System.out.println("register end");
    
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
    

4.2 SQLSessionFactory工具抽取:改进登录案例

  • 案例存在问题

    • SqlSessionFactory创建部分存在代码冗余
    • SqlSessionFactory内置连接池,多次创建资源消耗较大
  • 改进方案

    • 使用工具类解决代码重复问题
    • 使用静态代码块实现仅创建一次SqlSessionFactory
  • 代码实现

    • 工具类

      public class SqlSessionFactoryUtils {
      
          // 提升作用域,用于返回值
          private static SqlSessionFactory sqlSessionFactory;
      
          // 静态代码块会随着类的加载自动执行且只执行一次
          static {
              String resource = "mybatis-config.xml";
              InputStream inputStream = null;
              try {
                  inputStream = Resources.getResourceAsStream(resource);
              } catch (IOException e) {
                  e.printStackTrace();
              }
              sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          }
      
          // 返回工厂对象
          public static SqlSessionFactory getSqlSessionFactory() {
              return sqlSessionFactory;
          }
      
      }
      
    • 改进后代码(以LoginServlet为例)

      @WebServlet("/login")
      public class LoginServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      
              System.out.println("开始进行登录判断");
      
              // 接收用户名和密码
              String username = request.getParameter("username");
              String password = request.getParameter("password");
      
              // 解决Request中文乱码
              // 使用ISO-8859-1编码将乱码字符编回二进制码,得到其字节数组
              byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
              // 将字节数组使用UTF-8重新编码
              username = new String(bytes, StandardCharsets.UTF_8);
      
              // 调用MyBatis完成查询
              // 加载MyBatis核心配置文件,获取SqlSessionFactory,已通过工具类进行改进
              SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
              // 获取Session对象,设置自动提交事务
              SqlSession sqlSession = sqlSessionFactory.openSession(true);
              // 获取UserMapper
              UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
              // 执行SQL
              User user = userMapper.selectAll(username, password);
      
              // 设置response编码并获取字符输出流
              response.setContentType("text/html;charset=utf-8");
              PrintWriter writer = response.getWriter();
      
              // 处理结果
              if (user != null) {
                  // 登录成功
                  writer.write("login success" + user.getUsername() + "欢迎您");
              } else {
                  // 登录失败
                  writer.write("login fail 用户" + user.getUsername() + "不存在");
              }
              // 释放资源
              sqlSession.close();
              System.out.println("login end");
          }
      
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doGet(request, response);
          }
      }
      
      
posted @ 2022-09-28 16:48  Dandelion_000  阅读(128)  评论(0编辑  收藏  举报