Servlet

  • 是什么?

JavaWeb-项目创建

  • 基本创建流程

    | 创建流程 |

| :———————————————————-: |
| |

  • Servlet 实现步骤*

    1. 创建普通Java
    2. 实现Servlet 的规范,继承HttpServlet
    3. 重写service 方法,用来处理请求
    4. 设置注解,指定访问的路径
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.coderitl.javaservlet;

    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("/service")
    public class ServletTest extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Hello Servlet");
    System.out.println("Hello Servlet");
    }
    }

生命周期

  • servlet 没有main() 方法,不能独立运行,它的运行完全由Servlet 引擎来控制和调度,所谓生命周期,指的是Servlet 容器何时创建servlet 实例,何时调用其方法进行请求处理,何时并销毁其实例的整个过程

  • 实例和初始化时机

    • 当请求达到容器时,容器查找该servlet 对象是否存在,如果不存在,则会创建并实例并进行初始化
  • 就绪/调用/服务阶段

    • 有请求达到容器时,容器调用servlet 对象的service() 方法,处理请求的方法在整个生命周期中可以被多次调用,HttpServlet service 方法,会依据请求方式来调用doGet() 或者doPost 方法,但是,这两个do 方法默认情况下,会抛出异常,需要子类去override
  • 销毁时机

    • 当容器关闭时,会将程序中的Servlet 实例进行销毁,上述的生命周期可以通过Servlet 中的生命周期来观察,在Servlet 中有三个生命周期,不由用户手动调用,而是在特定的时机有容器自动调用,观察者三个生命周期方法,即可观察到servlet 的生命周期
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    package com.coderitl.javaservlet;

    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;

    /**
    * 实现 Servlet
    * 1. 创建普通 Java 类
    * 2. 实现 Servlet 的规范,继承 HttpServlet 类
    * 3. 重写 service 方法,用来处理请求
    * 4. 设置注解 指定访问的路径
    */


    @WebServlet("/service")
    public class ServletTest extends HttpServlet {
    /**
    * 就绪/服务方法(处理请求数据)
    * 系统方法,服务器自动调用
    * 当有请求到达 servlet时,就会调用该方法
    * 该方法可以被多次调用
    *
    * @param req
    * @param resp
    * @throws ServletException
    * @throws IOException
    */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    System.out.println("调用...");
    }


    /**
    * 销毁方法
    * 系统方法,服务器会自动调用
    * 当服务器关闭或应用程序停止时,调用该方法
    * 方法只会执行一次
    */
    @Override
    public void destroy() {
    super.destroy();
    System.out.println("销毁...");
    }


    /**
    * 初始化方法
    * 系统方法,服务器自动调用
    * 当请求达到 servlet 容器时,Servlet 容器会判断该 servlet 对象是否存在,如果不存在,则创建实例并初始化
    * 方法只会执行一次
    *
    * @throws ServletException
    */
    @Override
    public void init() throws ServletException {
    System.out.println("创建...");
    }
    }

Servlet-HttpServlet的两个对象

HttpServletRequest对象

HtepServletRequest 对象: 主要作用是用来接收客户端发送过来的请求信息,例如: 请求的参数,发送的头信息等

  • 常用方法

    方法名称 作用
    getRequestURL() 获取客户端发出请求时的完整URL
    getRequestURI 获取请求行中的资源名称部分(项目名称开始)
    getQueryString() 获取请求行中的参数部分
    getMethod() 获取客户端请求方式
    getProtocol() 获取HTTP 版本号
    getContexPath() 获取webapp 名字
    String getParameter(String name) 根据表单组件名称获取提交数据
    void setCharacterEncoding(String charset) 指定每个请求的编码

HttpServletResponse对象

  • 常用方法

    方法名称 作用
    setHeader(name,value) 设置响应信息头
    setContentType(String) 设置响应文件类型、响应式的编码格式
    setCharacterEncoding(String) 设置服务器端响应内容编码格式
    getWriter() 获取字符输出流

Servlet-JDBC-Pool

  • 数据库表结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    mysql> desc userinfo;
    +----------+-------------+------+-----+---------+----------------+
    | Field | Type | Null | Key | Default | Extra |
    +----------+-------------+------+-----+---------+----------------+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | username | varchar(10) | NO | | NULL | |
    | password | varchar(18) | NO | | NULL | |
    +----------+-------------+------+-----+---------+----------------+
    3 rows in set (0.12 sec)
  • 源码阅读

request 实现转发跳转

  • 转发作用在服务器,将请求发送给服务器上的其他资源,以共同完成一次请求的处理

  • 页面跳转

    在调用业务逻辑的Servlet 中,编写一下代码:

    1
    2
    3
    request.getRequestDisaptcher("/utl-pattern").forward(request,response);

    注意: 使用 forward 跳转时,是在服务器内部跳转,地址栏不发生变化,属于同一次请求
  • 数据传递

    forward 表示一次请求,是在服务器内部跳转,可以共享同一次 request 作用域中的数据

    • request 作用域: 拥有存储数据的空间,作用范围是一次请求有效
      • 可以将数据存入request 后,在一次请求过程中的任何位置进行获取
      • 可传递任何数据(基本数据类型,对象,数组,集合等)
    • 存数据: request.setAttribuite(key,value),以键值对存储在 request 作用域中,key string 类型,value Object 类型
    • 取数据: request.getAttribute(key)
      • 通过String 类型的key 访问Object 类型的value
  • 转发的特点

    • 转发是服务器行为
    • 转发是浏览器只做了一次访问请求
    • 转发浏览器地址不变
    • 转发两次跳转之间传输的信息不会丢失,所以可以通过request 进行数据传递
    • 转发只能将请求转发给同一个web 应用中的组件

重定向

  • 重定向作用在客户端,客户端将请求发送给服务器后,服务器响应给客户端一个新的请求地址,客户端重新发送请求

  • 页面跳转

    • 在调用业务逻辑的Servlet 中,编写以下代码

      1
      response.sendRedirect("/目标uri");
  • 数据传递

    sendRedirect 跳转时,地址栏发生变换,代表客户端重新发送新的请求,属于两次请求

    • response 没有作用域,两次request 请求中的数据无法共享
    • 传递数据: 通过uri 的拼接进行数据传递/b?xxx=value
    • 获取数据: request.getParameter("key")

Servlet线程安全问题

  • 线程安全问题

    Servlet 在访问之后,会执行实例化操作,创建一个Servlet 对象,而我们Tomcat 容器可以同时多个线程并发访问同一个Servlet,如果在方法中对成员变量做修改操作,就会有线程安全的问题

  • 如何保证线程安全

    • synchronized
      • 将存在线程安全问题的代码放在同步代码块中
    • 实现singleThreadMode 接口
      • Servlet 实现singleThreadMode 接口后,每个线程都会创建Servlet 实例,这样每个客户端请求就不存在共享资源的问题,但是Servlet 响应客户端请求的效率太低,所以已经淘汰
      • 尽可能使用局部变量

状态管理

  • 分类

    • 客户端状态管理技术: 将状态保存在客户端,代表性的是Cookie 技术
    • 服务器状态管理技术: 将状态保存在服务器端,代表性的是Session 技术(服务器传递session 时需要使用sessionID 时需要使用Cookie 的方式)application

Cookie的使用

Session的使用

  • 概述

    • Session 用于记录用户的状态,Session 指的是在一段时间内,单个客户端与 Web 服务器的一连串相关的交互过程

    • 在一个Session 中,客户可能会多次请求访问同一个资源,也有可能请求访问各种不同的服务器资源

  • Session原理

    • 服务器会为每一次会话分配一个Session 对象

    • 同一个浏览器发起的多次请求,同属于一次会话(Session)

    • 首次使用到Session 时,服务器会自动创建Sessoin,并创建Cookie 存储SessionID 发送回客户端

  • Session的使用

    • Session 作用域: 拥有存储数据的空间,作用范围是一次会话有效
      • 一次会话是使用同一浏览器发送的多次请求,一旦浏览器关闭,则会话结束
      • 可以将数据存入Session 中,在一次会话的任意位置进行获取
      • 可传递任何数据(基本数据类型,对象,集合,数组)
  • 基本使用

    1
    2
    3
    HttpSession session = request.getSession();
    // 获取 SessionID
    System.out.println(session.getId());
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 设置
    HttpSession session = request.getSession();
    // 1. 使用 Session 保存数据
    session.setAttribute("username", "coder-itl");
    // 获取 sessionId
    System.out.println(session.getId());


    // 获取 Session
    HttpSession session = request.getSession();

    String username = (String) session.getAttribute("username");
    System.out.println("从Session中获得了: " + username);


    // 移除 Session
    HttpSession session = request.getSession();
    session.removeAttribute("username");


  • SessionRequest应用区别

    • request 是一次请求有效,请求改变,则request 改变

    • session 是一次会话有效,浏览器改变,则session 改变

  • Session的生命周期

    • 开始: 第一次使用到Session 的请求产生,则创建Session

    • 结束:

      • 浏览器关闭,则失效

      • Session 超时,则失效

        • session.setMaxInactiveInterval(seconds); // 设置最大有效时间(单位: 秒)
      • 手工销毁,则失效

        • session.invalidate(); // 登录退出,注销

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          // 设置生命周期 lifeServlet
          session.setMaxInactiveInterval(10);
          System.out.println(session.getId()); // 10s之前: F1F211AA96207EE7EA51F66D83131C70

          // GetServlet
          HttpSession session = request.getSession();
          // 查看 10s 后的 sessionId
          System.out.println(session.getId()); // 10s之前: F1F211AA96207EE7EA51F66D83131C70

          再次获取:
          10s之后: 80F991614ED7C969A895D5952B4C4693

浏览器禁用Cookie 解决方案

  • 浏览器禁用Cookie 的后果

    服务器在默认情况下,会使用Cookie 的方式将SessionId 发送给浏览器,如果用户禁用Cookie,sessionId 不会被浏览器保存,此时,服务器可以使用URL 充血这样的方式来发送SessionId

  • URL 重写

    浏览器在访问服务器上的某个地址时,不再使用原来的那个地址,而是使用经过改写的地址,在原来的地址后面加上sessionId

  • 实现URL重写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    response.encodeRedirectURL(String url) 生成重写的 URL

    -----------------------------------------------------

    HttpSession session = request.getSession();
    // 获取新的 url
    String newUrl = response.encodeRedirectURL("MavenProject_war_exploded/getS");
    System.out.println(newUrl);
    // 重定向到带有 sessionId 的URL
    response.sendRedirect(newUrl);

ServletContext对象

获取ServletContext对象的三种方式

  • 获取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     // 获取 ServletContext 对象 推荐
    ServletContext servletContextThis = this.getServletContext();

    // 推荐写法
    ServletContext servletContextReq = req.getServletContext();

    HttpSession session = req.getSession();
    ServletContext servletContextSession = session.getServletContext();

    /* org.apache.catalina.core.ApplicationContextFacade@42d820 */
    System.out.println(servletContextThis);
    /* org.apache.catalina.core.ApplicationContextFacade@42d820 */
    System.out.println(servletContextReq);
    /* org.apache.catalina.core.ApplicationContextFacade@42d820 */
    System.out.println(servletContextSession);

ServletContext作用

  • 获取项目真实路径

    1
    2
    3
    4
    5
    6
    获取当前项目在服务器发布的真实路径
    /* 获取项目真实路径 */
    String realPath = servletContextThis.getRealPath("/");
    /* This project realPath input: E:\Code\Java\VerificationCode\target\VerificationCode-1.0-SNAPSHOT\ */
    System.out.println("This project realPath input: " + realPath);

  • 获取项目上下文路径

    1
    2
    3
    4
    5
    6
    7
    获取当前项目上下文路径(应用程序名称)
    /* 获取上下文路径 */
    String contextPath = request.getContextPath();
    String contextPath = servletContextThis.getContextPath();
    /* Get context path output: /VerificationCode_war_exploded */
    System.out.println("Get context path output: " + contextPath);

  • 全局容器

    1
    ServletContext 拥有作用域,可以存储数据到全局容器中
    • 存储数据: servletContextThis.setAttribute("name",value);
    • 获取数据: servletContextThis.getAttribute("name");
    • 移除数据: servletContextThis.removeAttribute("name");
  • ServletContext 特点

    • 唯一性: 一个应用对应一个ServletContext
    • 生命周期: 只要容器不关闭或者应用不卸载,ServletContext 就一直在

Filter 使用

  • 含义

    过滤器(Filter)是处于客户端与服务器目标资源之间的一道过滤技术

  • 执行流程

    Filter执行流程

  • 执行地位在Servlet 之前,客户端发送请求时,会经过Filter,再达到目标Servlet 中,响应时,会根据执行流程再次反向执行Filter
  • 可以解决多个Servlet 共性代码的冗余问题(Eg:乱码处理、登录验证)

Servlet API 中提供了一个Filter 接口,开发人员会编写一个Java 类实现这个接口即可,这个Java 类称为过滤器

  • 实现过程

    • 编写Java 类实现Filter 接口
    • doFilter 方法中编写拦截逻辑
    • 设置拦截路径
  • 基本使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Filter:

    package com.example.filter;
    // Filter包在 javax.servlet.
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;

    @WebFilter("/TestServlet")
    public class FilterTest implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("------Filter-----------");
    // 放行
    chain.doFilter(request, response);

    System.out.println("----------end--------------");
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    Servlet:

    package com.example.controller;

    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;

    @WebServlet(name = "TestServlet", value = "/TestServlet")
    public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    System.out.println("--------------Servlet------------");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
    }
    }

  • 实际执行流程分析

    分析Filter执行流程

  • xml 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    web.xml:


    <filter>
    <!-- 名称 -->
    <filter-name>FilterName</filter-name>
    <!-- 过滤器类全称 -->
    <filter-class>com.example.filter.XMLFilter</filter-class>
    </filter>
    <!-- 映射路径配置 -->
    <filter-mapping>
    <filter-name>FilterName</filter-name>

    <url-pattern>/*</url-pattern>
    </filter-mapping>
  • 执行分析

    xml配置Filter