文章目录


8.1 官方文档

8.1.1 地址: https://tomcat.apache.org/tomcat-8.0-doc/servletapi/index.html

8.1.2 离线文档: JAVA_EE_api_中英文对照版.chm

8.1.3 Servlet 和 Tomcat 的关系: 一句话, Tomcat 支持 Servlet

8.2 为什么会出现 Servlet

  • 提出需求: 请用你现有的html css javascript,开发网站,比如可以让用户留言/购物/支付, 你能搞定吗?
  • 引入我们动态网页(能和用户交互)技术 => Servlet
  • 对Java Web 技术体系的流程图改造说明(细化)

在这里插入图片描述

8.3 什么是 Servlet

  • 什么是Servlet ?

    ① Servlet 在开发动态 WEB 工程中,得到广泛的应用 [非常重要]
    ② Servlet(基石)是 SpringMVC 的基础

  • Servlet (java 服务器小程序),它的特点:

  1. 他是由服务器端调用和执行的(一句话:是Tomcat解析和执行)
  2. 他是用java语言编写的,本质就是Java类
  3. 他是按照Servlet (接口)规范开发的 (除了tomcat->Servlet weblogic->Servlet)
  4. 功能强大,可以完成几乎所有的网站功能(在以前,老程员,使用Servlet开发网站)
  5. 不过,现在技术栈要求高,只用Servlet不行了,框架的底层还是用的 Servlet

8.4 Servlet 在 JavaWeb 项目位置

在这里插入图片描述

8.5 Servlet 基本使用

8.5.1 Servlet 开发方式说明

  1. servlet3.0 前使用 web.xml ,servlet3.0 版本以后(包括 3.0)支持注解, 同时支持 web.xml配置
  2. 如何查看 servlet 版本

在这里插入图片描述

  1. 后面学习 SpringBoot 时,我们用注解方式, 从 ssm,springboot 后面全部使用注解
  2. 这里专门讲 servlet,为让大家更清晰知道 servlet 使用原理,用配置方式(说明,原生的 Servlet 在项目中使用很少)
  3. 不管使用哪种方式,本质都一样

8.5.2 快速入门- 手动开发 Servlet

  • 需求说明
  1. 开发一个 HelloServlet
  2. 当浏览器 访问 http://localhost:8080/web 应用/helloServlet 时,后台输出 “hi~ HelloServelt”
  • 具体步骤
  1. 编写类HelloServlet去实现 Servlet 接口
  2. 实现 service 方法,处理请求,并响应数据
  3. 在 web.xml 中去配置 servlet 程序的访问地址
  • 实现
  1. 创建servlet JavaWeb工程,并配置好Tomcat
  2. 添加servlet-api.jar(在tomcat/lib下) 到工程,因为servlet.jar 不是jdk自带的,要引入

在这里插入图片描述

  1. 在src 下 包 com.xjs.servlet.HelloServlet.java ,并实现Servlet接口
package com.xjs.servlet;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/19-02-19-14:26
 * @Version: 1.0
 */

/**
 * 解读:
 * 1.开发一个servlet 需要实现 Servlet 接口
 * 2.实现Servlet接口的5个方法
 */

public class HelloServlet implements Servlet {

    //HttpServlet

    private int count = 0; //属性

    /**
     * 1.初始化 servlet
     * 2.当 Tomcat 创建 HelloServlet 实例时,会调用 init 方法
     * 3.该方法只会被调用一次
     *
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init() 被调用~~~");
    }

    /**
     * 返回 ServletConfig 对象,也就是返回 Servlet 的配置
     *
     * @return
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 1.service 方法处理浏览器的请求(包括 get/post)
     * 2.当浏览器每次请求 Servlet 时,就会调用一次 service 方法
     * 3.当 tomcat 调用该方法时,会把 http 请求封装成 实现了 ServletRequest 接口的 request 对象
     * 4.通过 servletRequest 对象,可以得到用户提交的数据
     * 5.servletResponse 对象可以用于返回数据给 tomcat --> 浏览器
     *
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        count++;
        //如果count的值,在不停的累计,说明HelloServlet是单例的
        System.out.println("service() 被调用~~~ count= " + count);
        //tomcat每处理一次HTTP请求,就生成一个新的线程
        System.out.println("当前线程id= " + Thread.currentThread().getId());

        //GET 和 POST 请求的分发处理
        //思路:从 servletRequest对象来获取请求方式 -> 找到其子类或者子接口
        //1. servletRequest对象 没有得到 提交方法 的方法
        //2. 要想到看看 servletRequest 的子接口有没有相关方法
        //3. 小技巧:ctrl + alt + B 就可以看到接口的子接口和实现类
        //4. 把servletRequest接口转成 HttpServletRequest 引用
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String method = httpServletRequest.getMethod();
        //System.out.println(method); //GET
        if ("GET".equals(method)) {
            doGet(); //用doGet() 处理GET请求
        } else if ("POST".equals(method)) {
            doPost(); //用doPost() 处理POST请求
        }

    }

    /**
     * 用于响应get请求
     */
    public void doGet() {
        System.out.println("doGet() 被调用~~");
    }

    /**
     * 用于响应post请求
     */
    public void doPost() {
        System.out.println("doPost() 被调用~~");
    }

    /**
     * 返回 servlet 信息,使用较少
     *
     * @return
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 1.该方法是在 servlet 销毁时,被 tomcat 调用
     * 2.只会调用一次
     */
    @Override
    public void destroy() {
        System.out.println("destroy() 被调用...");
    }
}

  1. 在web.xml配置HelloServlet,即:给HelloServlet 提供对外访问地址
<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!--小技巧:web.xml 文件主要用来配置该web应用到的Servlet-->
    <!--配置 HelloServlet
        解读:
        1.servlet-name:给Servlet取名(程序员来决定),该名字是唯一的,不能重复
        2.servlet-class:Servlet的类的全路径,因为Tomcat 在反射生成该Servlet时,需要使用
        3.url-pattern:这个就是该servlet访问的url的匹配(路径)
        4.这时我们应该这样访问该servlet:http://localhost:8080/servlet/helloServlet
        5.url-pattern 取名是程序员来决定的,注意 / 不能丢
        6.一切配置完成后,记得要 redeploy 或者 restart,然后浏览器再访问HelloServlet
        7.load-on-startup:表示tomcat启动时,会自动加载servlet实例, 1 表示加载的顺序

        小技巧: alt + R ==> 自己配的快捷键(settings ==> Keymap)
    -->
    <servlet>
        <servlet-name>HelloServlet</servlet-name> <!--一般以类名作为名字-->
        <servlet-class>com.xjs.servlet.HelloServlet</servlet-class>
        <!--<load-on-startup>1</load-on-startup>  表示tomcat启动时,会自动加载servlet实例 -->
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name> <!--这里的名字要和上面的保持一致-->
        <url-pattern>/helloServlet</url-pattern>
    </servlet-mapping>
  1. 通过浏览器访问HelloServlet ,看是否正确

在这里插入图片描述


在这里插入图片描述

8.5.3 浏览器调用 Servlet 流程分析

  • 一图胜千言

在这里插入图片描述

8.5.4 Servlet 生命周期

  • 主要有三个方法:
  1. init() 初始化阶段
  2. service() 处理浏览器请求阶段
  3. destroy() 终止阶段
  • 示意图(比较重要,而且形象)

在这里插入图片描述


  • 初始化阶段

    • Servlet 容器 (比如: Tomcat) 加载 Servlet
    • 加载完成后,Servlet 容器会创建一个 Servlet 实例并调用 init()方法
    • init()方法只会调用一次
  • Servlet 容器在下面的情况装载 Servlet :

    ① Servlet 容器(Tomcat)启动时自动装载某些 servlet,实现这个需要在 web.xml 文件中添加<load-on-startup>1</load-on-startup> 1 表示装载的顺序
    ② 在 Servlet 容器启动后,浏览器首次向 Servlet 发送请求(这个前面说过)
    ③ Servlet 重新装载时(比如 tomcat 进行 redeploy,redeploy 会销毁所有的 Servlet 实例),浏览器再向 Servlet 发送请求的第 1 次

在这里插入图片描述

  • 处理浏览器请求阶段(service 方法)
  1. 每收到一个 http 请求,服务器就会产生一个新的线程去处理
  2. 创建一个用于封装 HTTP 请求消息的 ServletRequest 对象和一个代表 HTTP 响应消息的ServletResponse 对象
  3. 然后调用 Servlet 的 service()方法并将请求和响应对象作为参数传递进去
  • 终止阶段 destory 方法(体现 Servlet 完整的生命周期)

    • web 应用被终止
    • 或者 Servlet 容器终止运行
    • 或者 Servlet 类重新装载时
    • 都会调用destroy()方法,比如重启 tomcat,或者 redeploy web 应用

8.5.5 GET 和 POST 请求的分发处理

  • 开发 Servlet,通常编写 doGet、doPost 方法
  • 来对表单的 get 和 post 请求进行分发处理
  • 代码演示
  1. 创建 E:\javaweb_project\servlet\web\register.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/helloServlet" method="post">
    user: <input type="text" name="username"/><br/><br/>
    <input type="submit" value="注册用户"/>
</form>
</body>
</html>
  1. 修改 E:\javaweb_project\servlet\web\src\com\xjs\servlet\HelloServlet.java,核心代码, 修改 HelloServlet.java
/**
     * 1.service 方法处理浏览器的请求(包括 get/post)
     * 2.当浏览器每次请求 Servlet 时,就会调用一次 service 方法
     * 3.当 tomcat 调用该方法时,会把 http 请求封装成 实现了 ServletRequest 接口的 request 对象
     * 4.通过 servletRequest 对象,可以得到用户提交的数据
     * 5.servletResponse 对象可以用于返回数据给 tomcat --> 浏览器
     *
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        count++;
        //如果count的值,在不停的累计,说明HelloServlet是单例的
        System.out.println("service() 被调用~~~ count= " + count);
        //tomcat每处理一次HTTP请求,就生成一个新的线程
        System.out.println("当前线程id= " + Thread.currentThread().getId());

        //GET 和 POST 请求的分发处理
        //思路:从 servletRequest对象来获取请求方式 -> 找到其子类或者子接口
        //1. servletRequest对象 没有得到 提交方法 的方法
        //2. 要想到看看 servletRequest 的子接口有没有相关方法
        //3. 小技巧:ctrl + alt + B 就可以看到接口的子接口和实现类
        //4. 把servletRequest接口转成 HttpServletRequest 引用
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String method = httpServletRequest.getMethod();
        //System.out.println(method); //GET
        if ("GET".equals(method)) {
            doGet(); //用doGet() 处理GET请求
        } else if ("POST".equals(method)) {
            doPost(); //用doPost() 处理POST请求
        }

    }

8.5.6 通过继承 HttpServlet 开发 Servlet

  • HttpServlet 介绍

在实际项目中,都是使用继承 HttpServlet 类开发 Servlet 程序,更加方便

在这里插入图片描述

  • 应用实例
  1. 通过继承 HttpServlet 开发一个 HiServlet
  2. 当浏览器 访问 http://localhost:8080/web 应用名/hiServlet 时,后台输出 “hi HiServelt”
  • 具体的开发步骤
  1. 编写一个类去继承 HttpServlet 类
  2. 根据业务需要重写 doGet 或 doPost 方法
  3. 到 web.xml 中的配置 Servlet 程序
  • 代码实现
  1. 创建 HiServlet.java
package com.xjs.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/21-02-21-8:12
 * @Version: 1.0
 */
public class HiServlet extends HttpServlet {

    //重写 HttpServlet 的 doGet 和 doPost 方法

    /**
     * 处理doGet请求
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HiServlet doGet() ~~~");
    }

    /**
     * 处理doPost请求
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HiServlet doPost() ~~~");
    }
}

  1. 修改 web.xml 完成配置
<!--配置HiServlet-->
    <servlet>
        <servlet-name>HiServlet</servlet-name>
        <servlet-class>com.xjs.servlet.HiServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HiServlet</servlet-name>
        <url-pattern>/hiServlet</url-pattern>
    </servlet-mapping>
  1. 完成测试

8.5.7 IDEA 开发 Servlet 程序

  • 编手动开发 Servlet 需要程序员自己配置 Servlet 比较麻烦

  • 在工作中,直接使用 IDEA 开发 Servlet 会更加方便

  • 应用实例

  1. 创建 OkServlet.java

在这里插入图片描述

在这里插入图片描述

package com.xjs.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/21-02-21-8:41
 * @Version: 1.0
 */
public class OkServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("OkServlet  doPost()");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("OkServlet  doGet()");
    }
}

<!--这是idea自动生成的配置-->
    <servlet>
        <servlet-name>OkServlet</servlet-name>
        <servlet-class>com.xjs.servlet.OkServlet</servlet-class>
    </servlet>
    <!--对OkServlet进行url配置-->
    <servlet-mapping>
        <servlet-name>OkServlet</servlet-name>
        <url-pattern>/okServlet</url-pattern>
    </servlet-mapping>

在这里插入图片描述

  1. 完成测试

8.5.8 Servlet 注意事项和细节

  1. Servlet 是一个供其他 Java 程序 (Servlet 引擎) 调用的 Java 类,不能独立运行
  2. 针对浏览器的多次 Servlet 请求,通常情况下,服务器只会创建一个 Servlet 实例对象
  3. 也就是说 Servlet 实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务
  4. 直至 web 容器退出/或者 redeploy 该 web 应用,servlet 实例对象才会销毁

在这里插入图片描述

  1. 在 Servlet 的整个生命周期内,init 方法只被调用一次,而对每次请求都导致 Servlet 引擎调用一次 servlet 的 service 方法
  2. 对于每次访问请求,Servlet 引擎都会创建一个新的 HttpServletRequest 请求对象和一个新的HttpServletResponse 响应对象,然后将这两个对象作为参数传递给它调用的 Servlet的 service()方法,service 方法再根据请求方式分别调用 doXXX 方法
  3. 如果在<servlet>元素中配置了一个<load-on-startup>元素,那么 WEB 应用程序在启动时,就会装载并创建 Servlet 的实例对象、以及调用 Servlet 实例对象的 init()方法,聊聊(定时发送邮件的服务/自动启动->完成务)
<servlet>
        <servlet-name>invoker</servlet-name>
        <servlet-class>
            org.apache.catalina.servlets.InvokerServlet
        </servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

8.6 Servlet - 注解方式

8.6.1 快速入门

  1. 编写类OkServlet去继承HttpServlet
  2. 注解方式配置OkServlet, 一个Servlet支持配置多个urlPattern
  3. 代码实现
package com.xjs.servlet.annotation;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/21-02-21-9:04
 * @Version: 1.0
 * 注解方式来配置
 */

/**
 * 解读:
 * 小技巧:显示方法间的分隔线
 * 1. @WebServlet 是一个注解
 * 2. @WebServlet 源码:
 * @Target({ElementType.TYPE})
 * @Retention(RetentionPolicy.RUNTIME)
 * @Documented ==> 在javadoc工具生成文档时,会有记录
 * public @interface WebServlet {
 *     String name() default "";
 *
 *     String[] value() default {};
 *
 *     String[] urlPatterns() default {};
 *
 *     int loadOnStartup() default -1;
 *
 *     WebInitParam[] initParams() default {};
 *
 *     boolean asyncSupported() default false;
 *
 *     String smallIcon() default "";
 *
 *     String largeIcon() default "";
 *
 *     String description() default "";
 *
 *     String displayName() default "";
 * }
 * 3. urlPatterns 对应 web.xml 的 <url-pattern></url-pattern>
 * 4. {"ok1","ok2"} 可以给OkServlet配置多个 url-pattern
 * 5. 相当于这个@WebServlet(urlPatterns = {"/ok1","/ok2"}) 代替了 web.xml的配置
 *    底层使用了 反射 + 注解 + IO + 集合 来完成一个支撑
 * 6. 浏览器可以访问OkServlet时,可以这样写:http://localhost:8080/servlet/ok1 或者
 *    http://localhost:8080/servlet/ok2
 * 7. 我们可以根据 @interface WebServlet 源码知道可以配置哪些信息
 * web.xml init-param 在注解中,如何指定呢? 看源码,寻找思路
 *         <init-param>
 *             <param-name></param-name>
 *             <param-value></param-value>
 *         </init-param>
 *    源码==>思路:当注解里面又包含注解时
 *    initParams = {@WebInitParam(name = "xx",value = "yy"),@WebInitParam(name = "kk",value = "zz")}
 * 8. 注解方式开发Servlet 和 web.xml 配置Servlet 流程机制是一样的
 */
@WebServlet(urlPatterns = {"/ok1","/ok2"}, initParams = {@WebInitParam(name = "xx",value = "yy"),@WebInitParam(name = "kk",value = "zz")}) //注意 / 不能少,否则会报错
public class OkServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("注解方式 OkServlet init() 被调用");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("注解方式  OkServlet  doGet()");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("注解方式  OkServlet  doPost()");
    }
}

8.6.2 Servlet urlPattern 配置

8.6.2.1 精确匹配
  • 配置路径 : @WebServlet(“/ok/zs”)
  • 访问 servlet: localhost:8080/servlet/ok/zs
8.6.2.2 目录匹配
  • 配置路径 : @WebServlet(“/ok/*”)
  • 访问文件:
    • localhost:8080/servlet/ok/aaa
    • localhost:8080/servlet/ok/bbb
8.6.2.3 扩展名匹配
  • 配置路径 : @WebServlet(“*.action”)

  • 访问文件:

    • localhost:8080/xjs/xxx.action
    • localhost:8080/xjs/yyy.action
  • 特别提示: @WebServlet(“/*.action”) ,不能带 /,否则 tomcat 报错

8.6.2.4 任意匹配
  • 配置路径 : @WebServlet("/")@WebServlet("/*")

  • 访问文件:

    • localhost:8080/xjs/aaa
    • localhost:8080/xjs/bbb
    • localhost:8080/xjs/ccc
  • 特别提醒:/ 和 /* 的配置,会匹配所有的请求,这个比较麻烦,要避免

8.6.2.5 注意事项和使用细节
  1. 当 Servlet 配置了 “/”,会覆盖 tomcat 的 DefaultServlet,当其他的 utl-pattern 都匹配不上时 , 都会走这个Servlet,这样可以拦截到其它静态资源
  • 查看:tomcat/conf/web.xml ,配置的 DefaultServlet

在这里插入图片描述

<servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
  1. 当 Servelt 配置了 “/*”,表示可以匹配任意访问路径
  2. 提示: 建议不要使用 / 和 /* ,建议尽量使用精确匹配
  3. 优先级遵守: 精确路径 > 目录路径 > 扩展名路径 > /* > /

8.7 Servlet -阶段课后作业

  1. 使用 idea 开发一个 CatServlet ,要求完成

    ① 通过实现 Servlet 接口开发该 Servlet
    ② 要求访问 url 为 http://localhost:8080/servlet/cat
    ③ 在 Servlet 的 service 方法 ,可以输出 该 servlet 访问次数,并输出访问是 get 还是 post
    ④ 在 web.xml 配置,编写一个 login.html ,有登录表单

package com.xjs.servlet.homework;

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

/**
 * @Author: 谢家升
 * @Date: 2022/2/21-02-21-17:17
 * @Version: 1.0
 */
public class CatServlet implements Servlet {

    //定义一个记录访问次数的变量
    private int count = 0;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("CatServlet  init() 被调用~");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        count++;
        System.out.println("CatServlet service() 被调用 count= " + count);

        //快捷键:可以快捷在访问的文件切换
        //ctrl + alt + <- 回到上一层
        //ctrl + alt + -> 回到下一层

        //ServletRequest ==> HttpServletRequest
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String method = httpServletRequest.getMethod();

        //get post 请求的分发处理
        if ("GET".equals(method)) {
            doGet();
        } else if ("POST".equals(method)) {
            doPost();
        }

    }

    /**
     * 处理get请求
     */
    public void doGet() {
        System.out.println("CatServlet doGet() 被调用~~~");
    }

    /**
     * 处理Post请求
     */
    public void doPost() {
        System.out.println("CatServlet doPost() 被调用~~~");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

<!--配置CatServlet(作业1)-->
    <servlet>
        <servlet-name>CatServlet</servlet-name>
        <servlet-class>com.xjs.servlet.homework.CatServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CatServlet</servlet-name>
        <url-pattern>/cat</url-pattern>
    </servlet-mapping>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/cat" method="post">
    user: <input type="text" name="username"/><br/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>
  1. 使用 idea 开发一个 DogServlet,要求完成 梳理思路

    ① 通过继承 HttpServlet 开发该 Servlet
    ② 要求访问 url 为 http://localhost:8080/servlet/dog
    ③ 当访问时,分别输出 get 和 post 访问的次数(即分开统计)
    ④ 在 web.xml 配置,使用前面的 login.html,完成测试

package com.xjs.servlet.homework;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/21-02-21-17:54
 * @Version: 1.0
 */
public class DogServlet extends HttpServlet {

    private int count1 = 0;
    private int count2 = 0;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        count1++;
        System.out.println("DogServlet  doPost() 调用次数= " + count1);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        count2++;
        System.out.println("DogServlet  doGet() 调用次数= " + count2);


    }
}

<!--配置DogServlet(作业2)-->
    <servlet>
        <servlet-name>DogServlet</servlet-name>
        <servlet-class>com.xjs.servlet.homework.DogServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DogServlet</servlet-name>
        <url-pattern>/dog</url-pattern>
    </servlet-mapping>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/dog" method="post">
    user: <input type="text" name="username"/><br/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>
  1. 使用 idea 开发一个 PigServlet,要求完成

    ① 通过继承 HttpServlet 开发该 Servlet
    ② 要求访问 url 为 http://localhost:8080/servlet/pig1 或者 /pig2
    ③ 当访问时,分别输出 get 和 post 访问的次数(即分开统计) ,并输出浏览器/
    客户端的 ip
    ④ 通过注解配置,使用前面的 login.html,完成测试
    ⑤ 要求 PigServlet,在 Tomcat 启动时,就自动加载

package com.xjs.servlet.homework;

import javax.servlet.ServletConfig;
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;

/**
 * @Author: 谢家升
 * @Date: 2022/2/21-02-21-18:08
 * @Version: 1.0
 */
@WebServlet(urlPatterns = {"/pig1", "/pig2"}, loadOnStartup = 1)
public class PigServlet extends HttpServlet {

    private int getCount = 0;
    private int postCount = 0;

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("PigServlet  init() 被调用~");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //我是一个 OOP 程序员,一定要考虑使用对象,
        //如果这个对象没有,就去它的父类或者父接口去找

//        String localAddr = request.getLocalAddr();
//        System.out.println(localAddr);//127.0.0.1

        String remoteAddr = request.getRemoteAddr();
//        System.out.println(remoteAddr);//127.0.0.1

        System.out.println("PigServlet doGet() 调用次数= " + (++getCount) + " 浏览器IP= " + remoteAddr);


    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String remoteAddr = request.getRemoteAddr();
        //System.out.println(remoteAddr);//127.0.0.1

        System.out.println("PigServlet doPost() 调用次数= " + (++postCount) + " 浏览器IP= " + remoteAddr);


    }


}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/pig2" method="post">
    user: <input type="text" name="username"/><br/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>
  1. 自己手动画出访问 login.html 的完整 UML 时序图

在这里插入图片描述

8.8 关联篇:HTTP 协议!!!

==> 文章链接


8.9 ServletConfig

8.9.1 ServletConfig 基本介绍

  1. ServletConfig 类是为 Servlet 程序的配置信息的类
  2. Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建
  3. Servlet 程序默认是第 1 次访问的时候创建,ServletConfig 在 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象

8.9.2 ServletConfig 类能干什么

  1. 获取 Servlet 程序的 servlet-name 的值
  2. 获取初始化参数 init-param
  3. 获取 ServletContext 对象

8.9.3 ServletConfig 应用实例

  • 需求: 编写 DBServlet.java 完成如下功能
  1. 在 web.xml 配置连接 mysql 的用户名和密码
  2. 在 DBServlet 执行 doGet()/doPost() 时,可以获取到 web.xml 配置的用户名和密码

在这里插入图片描述

  1. 示意图(思路分析)

在这里插入图片描述

  • 代码实现
package com.xjs.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/24-02-24-9:09
 * @Version: 1.0
 */
public class DBServlet extends HttpServlet {

    /**
     * 梳理 ServletConfig config 使用流程
     * 1.当 DBServlet 对象初始化时,Tomcat会同时创建一个 ServletConfig对象
     * 2.这时如果 DBServlet init() 方法中你调用了 super.init(config);
     * 3.调用 父类 GenericServlet
     * public void init(ServletConfig config) throws ServletException {
     *         this.config = config;
     *         this.init();
     *     }
     *     这时就会把 Tomcat创建的这个 ServletConfig 对象赋给 GenericServlet的属性 config
     * 4.因此如果你重写了 init() 方法,如果你在其他方法通过 getServletConfig() 方法获取 ServletConfig 对象
     *   则一定要记住,调用 super.init(config);
     *
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init() config= "+config);
        super.init(config);//这里如果不写该方法,下面 52 行代码将会空指针
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //DBServlet 执行 doGet()/doPost() 时,可以获取到web.xml配置的用户名和密码
        //你是OOP程序员 --> 借助现有的方法或对象来搞定
        //DBServlet 的父类 GenericServlet 有 getServletConfig()
        /**
         * 解读:
         * 1.getServletConfig() 是 GenericServlet 的一个方法
         * 2.返回的 servletConfig 是 GenericServlet 的 一个属性:private transient ServletConfig config;
         * 3.当一个属性被 transient 修饰,表示该属性不会被串行化(有些重要信息,不希望保存到文件)
         */
        ServletConfig servletConfig = getServletConfig();
        System.out.println("doPost() config= " + servletConfig);
        String username = servletConfig.getInitParameter("username");
        String pwd = servletConfig.getInitParameter("pwd");
        System.out.println("初始化参数username= " + username);
        System.out.println("初始化参数pwd= " + pwd);


    }

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

<!--配置DBServlet-->
    <servlet>
        <servlet-name>DBServlet</servlet-name>
        <servlet-class>com.xjs.servlet.DBServlet</servlet-class>
        <!--配置信息,而不是硬编码到程序-->
        <init-param>
            <param-name>username</param-name>
            <param-value>xjs</param-value>
        </init-param>
        <init-param>
            <param-name>pwd</param-name>
            <param-value>123456</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>DBServlet</servlet-name>
        <url-pattern>/db</url-pattern>
    </servlet-mapping>

8.10 ServletContext

8.10.1 为什么需要 ServletContext

  • 需求: 统计某个 web 应用的所有 Servlet 被访问的次数,怎么办?
  • 方案 1-DB

在这里插入图片描述


  • 方案 2-ServletContext

在这里插入图片描述


8.10.2 ServletContext 基本介绍

  1. ServletContext 是一个接口,它表示 Servlet 上下文对象
  2. 一个 web 工程,只有一个 ServletContext 对象实例
  3. ServletContext 对象 是在 web 工程启动的时候创建,在 web 工程停止的时销毁
  4. ServletContext 对象可以通过ServletConfig.getServletContext() 方法获得对ServletContext对象的引用,也可以通过this.getServletContext()来获得其对象的引用
  5. 由于一个 WEB 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象之间可以通过ServletContext 对象来实现多个 Servlet 间通讯
  6. ServletContext 对象通常也被称之为域对象

在这里插入图片描述

8.10.3 ServletContext 可以做什么

  1. 获取 web.xml 中配置的上下文参数 context-param 【信息和整个 web 应用相关,而不是属于某个 Servlet】
  2. 获取当前的工程路径,格式: /工程路径,比如 /servlet
  3. 获取工程部署后在服务器硬盘上的绝对路径,比如:E:\javaweb_paoject\servlet\out\artifacts\servlet_war_exploded
  4. 像 Map 一样存取数据,多个 Servlet 共享数据

在这里插入图片描述

8.10.4 应用实例 1-获取工程相关信息

  • 需求如下:
  1. 获取 web.xml 中配置的上下文参数 context-param
  2. 获取当前的工程路径,格式: /工程路径
  3. 获取工程部署后在服务器硬盘上的绝对路径
  • 代码实现
package com.xjs.servlet.servletcontext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/25-02-25-8:07
 * @Version: 1.0
 */
public class ServletContext_ extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //获取web.xml的context-parameter

        //1.获取servletContext对象
        ServletContext servletContext = getServletContext();
        //2.获取相应的值
        String website = servletContext.getInitParameter("website");
        String company = servletContext.getInitParameter("company");
        //3.获取项目的工程路径
        String contextPath = servletContext.getContextPath();
        //4.获取项目发布后,真正的工作路径
        //  / 表示我们的项目(发布后)的 根路径  E:\javaweb_project\servlet\out\artifacts\servlet_war_exploded\
        String realPath = servletContext.getRealPath("/");
        System.out.println("项目路径= " + contextPath);//  /servlet
        System.out.println("website= " + website);
        System.out.println("company= " + company);
        System.out.println("项目发布后的绝对路径= " + realPath); // E:\javaweb_project\servlet\out\artifacts\servlet_war_exploded\



    }

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

<!--配置ServletContext_-->
    <servlet>
        <servlet-name>ServletContext_</servlet-name>
        <servlet-class>com.xjs.servlet.servletcontext.ServletContext_</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletContext_</servlet-name>
        <url-pattern>/servletContext_</url-pattern>
    </servlet-mapping>

<!--配置整个网站的信息-->
    <context-param>
        <param-name>website</param-name>
        <param-value>http://www.xjs.net</param-value>
    </context-param>
    <context-param>
        <param-name>company</param-name>
        <param-value>红红火火</param-value>
    </context-param>

8.10.5 应用实例 2-简单的网站访问次数计数

8.10.5.1 需求分析/图解
  1. 需求: 完成一个简单的网站访问次数计数器:

在这里插入图片描述

  1. 使用 Chrome 访问 Servlet01, 每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示
  2. 使用火狐访问 Servlet02,每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示
package com.xjs.servlet.servletcontext;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/2/25-02-25-9:15
 * @Version: 1.0
 */
public class OrderServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


        //获取ServletContext对象
        ServletContext servletContext = getServletContext();
        //System.out.println("OrderServlet servletContext= " + servletContext + " 运行类型= " + servletContext.getClass());
        //OrderServlet servletContext= org.apache.catalina.core.ApplicationContextFacade@2e36e195 运行类型= class org.apache.catalina.core.ApplicationContextFacade

        从 ServletContext 获取 visit-count 属性 【类似hashMap的 k-v】
        //Object visit_count = servletContext.getAttribute("visit_count");
        判断visit_count是否为null
        //if (visit_count == null) { //第一次访问
        //    servletContext.setAttribute("visit_count", 1);
        //    visit_count = 1;
        //} else {
        //    visit_count = Integer.parseInt(visit_count + "") + 1;
        //    servletContext.setAttribute("visit_count", visit_count);
        //}

        Integer visit_count = WebUtils.visitCount(servletContext);

        //输出显示
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("<h1>网站被访问次数是:" + visit_count + "</h1>");
        writer.flush();
        writer.close();

    }

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

package com.xjs.servlet.servletcontext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/2/25-02-25-9:17
 * @Version: 1.0
 */
public class PayServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //获取ServletContext对象
        ServletContext servletContext = getServletContext();
        //System.out.println("PayServlet servletContext= " + servletContext + " 运行类型= " + servletContext.getClass());
        //PayServlet servletContext= org.apache.catalina.core.ApplicationContextFacade@2e36e195 运行类型= class org.apache.catalina.core.ApplicationContextFacade

        从 ServletContext 获取 visit-count 属性 【类似hashMap的 k-v】
        //Object visit_count = servletContext.getAttribute("visit_count");
        判断visit_count是否为null
        //if (visit_count == null) { //第一次访问
        //    servletContext.setAttribute("visit_count", 1);
        //    visit_count = 1;
        //} else {
        //    visit_count = Integer.parseInt(visit_count + "") + 1;
        //    servletContext.setAttribute("visit_count", visit_count);
        //}

        Integer visit_count = WebUtils.visitCount(servletContext);

        //输出显示
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("<h1>网站被访问次数是:" + visit_count + "</h1>");
        writer.flush();
        writer.close();

    }

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


package com.xjs.servlet.servletcontext;

import javax.servlet.ServletContext;

/**
 * @Author: 谢家升
 * @Date: 2022/2/25-02-25-9:52
 * @Version: 1.0
 */
public class WebUtils {

    //这个方法就是对访问次数的一个累计,同时返回访问次数
    public static Integer visitCount(ServletContext servletContext) {
        //从 ServletContext 获取 visit-count 属性 【类似hashMap的 k-v】
        Object visit_count = servletContext.getAttribute("visit_count");
        //判断visit_count是否为null
        if (visit_count == null) { //第一次访问
            servletContext.setAttribute("visit_count", 1);
            visit_count = 1;
        } else {
            visit_count = Integer.parseInt(visit_count + "") + 1;
            servletContext.setAttribute("visit_count", visit_count);
        }
        return Integer.parseInt(visit_count + "");
    }
}

<!--配置OrderServlet-->
    <servlet>
        <servlet-name>OrderServlet</servlet-name>
        <servlet-class>com.xjs.servlet.servletcontext.OrderServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>OrderServlet</servlet-name>
        <url-pattern>/orderServlet</url-pattern>
    </servlet-mapping>
    <!--配置PayServlet-->
    <servlet>
        <servlet-name>PayServlet</servlet-name>
        <servlet-class>com.xjs.servlet.servletcontext.PayServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>PayServlet</servlet-name>
        <url-pattern>/payServlet</url-pattern>
    </servlet-mapping>
  1. 完成测试

8.11 HttpServletRequest

8.11.1 HttpServletRequest 介绍

  1. HttpServletRequest 对象代表客户端的请求
  2. 当客户端/浏览器通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这个对象中
  3. 通过这个对象的方法,可以获得客户端这些信息

8.11.2 HttpServletRequest 类图

在这里插入图片描述

在这里插入图片描述

8.11.3 HttpServletRequest 常用方法

  1. getRequestURI() 获取请求的资源路径,比如:http://localhost:8080/servlet/loginServlet
  2. getRequestURL() 获取请求的统一资源定位符 (绝对路径) ,比如:http://localhost:8080/servlet/loginServlet
  3. getRemoteHost() 获取客户端的 主机
  4. getRemoteAddr()获取客户端IP地址
  5. getHeader() 获取请求头
  6. getParameter() 获取请求的参数
  7. getParameterValues() 获取请求的参数 (多个值的时候使用),比如 checkbox 返回的数组
  8. getMethod() 获取请求的方式 GET 或 POS
  9. setAttribute(key, value) 设置域数据
  10. getAttribute(key) 获取域数据
  11. getRequestDispatcher() 获取请求转发对象, 请求转发的核心对象

8.11.4 HttpServletRequest 应用实例

  1. 需求说明: 在一个表单提交数据给 Servlet,然后在 Servlet 通过 HttpServletRequest 对象获取相关数据

在这里插入图片描述


  1. 创建 register_request.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/requestMethods"
      method="post">
    u: <input type="text" name="username"/><br><br>
    p: <input type="password" name="pwd"/><br><br>
    选择你喜欢的水果:
    <input type="checkbox" name="hobby" value="clz">车厘子
    <input type="checkbox" name="hobby" value="cm">草莓
    <input type="checkbox" name="hobby" value="yt">樱桃<br/><br/>
    <input type="submit" value="注册用户"/>
</form>
</body>
</html>
  1. 创建 HttpServletRequestMethods.java
package com.xjs.servlet.request;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/2/27-02-27-10:05
 * @Version: 1.0
 */
public class HttpServletRequestMethods extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //这里我们使用request对象,获取表单提交的各种数据
        System.out.println("HttpServletRequestMethods doPost() 被调用~~");

        /*==========================
            获取和HTTP请求头相关的信息
         ===========================*/

        System.out.println("请求的资源路径URI= " + request.getRequestURI());//  /servlet/requestMethods
        System.out.println("请求的统一资源定位符(绝对路径) URL= " + request.getRequestURL());
        // http://localhost:8080/servlet/requestMethods
        System.out.println("请求的客户端的IP地址= " + request.getRemoteAddr()); //127.0.0.1

        System.out.println("HTTP请求头Host= " + request.getHeader("Host")); //localhost:8080
        //如果我们希望得到请求头的相关信息,可以使用request.gerHeader("请求头字段")
        System.out.println("该请求的发起地址是= " + request.getHeader("Referer"));
        // http://localhost:8080/servlet/register_request.html

        //取出firefox
        String header = request.getHeader("User-Agent");
        String[] s = header.split(" ");
        //System.out.println(s[s.length-1]);//Firefox/97.0
        System.out.println("浏览器= " + s[s.length - 1].split("\\/")[0]); //Firefox

        System.out.println("请求方式= " + request.getMethod()); //POST

        /*==========================
            获取和请求参数相关的信息,注意要求在返回数据前,获取参数
         ===========================*/

        //解决参数的中文乱码问题,注意,要写在返回参数前
        request.setCharacterEncoding("utf-8");

        //1.获取表单数据【单个数据】
        //username=tom&pwd=123&hobby=clz&hobby=cm&hobby=yt
        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");
        String pwd1 = request.getParameter("pwd1");
        String[] hobbies = request.getParameterValues("hobby");

        //输出信息
        System.out.println("username= " + username);
        System.out.println("pwd= " + pwd);
        System.out.println("pwd1= " + pwd1);//null

        for (String hobby : hobbies) {
            System.out.println("喜欢的水果= " + hobby);
        }

        //返回接收到的信息给浏览器
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("提交的用户名= " + username);
        writer.flush();
        writer.close();

    }

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

  1. 修改 web.xml 添加配置
<!--配置HttpServletRequestMethods-->
    <servlet>
        <servlet-name>HttpServletRequestMethods</servlet-name>
        <servlet-class>com.xjs.servlet.request.HttpServletRequestMethods</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HttpServletRequestMethods</servlet-name>
        <url-pattern>/requestMethods</url-pattern>
    </servlet-mapping>
  1. 完成测试

8.11.5 HttpServletRequest 注意事项和细节

  1. 获取 doPost 参数中文乱码解决方案:注意 setCharacterEncoding("utf-8") 要写在request.getParameter()

在这里插入图片描述

  1. 注意:如果通过 PrintWriter writer,有返回数据给浏览器,建议将获取参数代码写在writer.print() 之前,否则可能获取不到参数值(doPost)
  2. 处理 http 响应数据中文乱码问题

在这里插入图片描述

  1. 再次理解 Http 协议响应 Content-Type 的含义,比如 text/plain ,application/x-tar

8.11.6 课堂小练习

  1. 请通过 HttpServletRequest 对象获取到,是什么浏览器访问服务器(前面案例有)
  2. 请想办法获取到 JSESSIONID 的值

在这里插入图片描述


package com.xjs.servlet.request.homework;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: 谢家升
 * @Date: 2022/2/28-02-28-7:20
 * @Version: 1.0
 */
public class ComputerServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ComputerServlet 被调用~~");

        //解读:
        // (1) Windows NT 10.0; Win64; x64; rv:97.0
        // (2) Windows NT 10.0 和 Win64
        // (3) 回顾使用正则表达式
        String userAgent = request.getHeader("User-Agent");
        //java基础 正则表达式
        String regStr = "\\((.*)\\)";
        Pattern compile = Pattern.compile(regStr);
        Matcher matcher = compile.matcher(userAgent);
        matcher.find();// 因为我们的userAgent只有一组 ()
        String group = matcher.group(0);// (Windows NT 10.0; Win64; x64; rv:97.0)
        String group1 = matcher.group(1);// Windows NT 10.0; Win64; x64; rv:97.0
        String[] operInfos = group1.split(";");
        System.out.println("操作系统=" + operInfos[0]);//Windows NT 10.0
        System.out.println("操作系统位数=" + operInfos[1].trim());// Win64


    }

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

8.11.7 请求转发

8.11.7.1 为什么需要请求转发
  1. 目前我们学习的都是一次请求,对应一个 Servlet, 如图

在这里插入图片描述


  1. 但是在实际开发中,往往业务比较复杂,需要在一次请求中,使用到多个 Servlet 完成一个任务(Servlet 链, 流水作业) 如图:

在这里插入图片描述


8.11.7.2 请求转发说明
  1. 实现请求转发:请求转发指一个 web 资源收到客户端请求后,通知服务器去调用另外一个 web 资源进行处理

  2. HttpServletRequest 对象(也叫 Request 对象)提供了一个 getRequestDispatcher() 方法,该方法返回一个 RequestDispatcher 对象,调用这个对象的 forward() 方法可以实现请求转发

  3. request 对象同时也是一个域对象,开发人员通过 request 对象在实现转发时,把数据通过 request 对象带给其它 web 资源处理

    ● setAttribute 方法
    ● getAttribute 方法
    ● removeAttribute 方法
    ● getAttributeNames 方法

8.11.7.3 实现请求转发
  • 请求转发原理示意图

在这里插入图片描述


8.11.7.4 请求转发应用实例
  • 需求说明/图解, 如果是 tom,提示为管理员,其它是普通用户

在这里插入图片描述

在这里插入图片描述


  • 代码实现
  1. 开发一个登录页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/checkServlet" method="post">
    user: <input type="text" name="username"/><br/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>
  1. 创建 CheckServlet.java
package com.xjs.servlet.request;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/27-02-27-21:18
 * @Version: 1.0
 */
public class CheckServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("CheckServlet 被调用~~");

        //根据用户名来确定该用户是什么身份
        String username = request.getParameter("username");
        //注意:如果是同一个request对象(请求转发),那么可以在不同的servlet中,使用 getParameter()
        if ("tom".equals(username)) {
            //分配
            request.setAttribute("role", "管理员");
        } else {
            request.setAttribute("role", "普通用户");
        }

        //获取分发器
        //解读:
        //(1)  /manageServlet 写的是 要转发的 servlet 的 url
        //(2)  / 会被解析成 /servlet
        //(3)  forward(request,response) 表示把当前 servlet 的 request对象 和 response对象,传递给下一个 servlet使用
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/manageServlet");

        //把当前 servlet 的 request对象 和 response对象,传递给下一个 servlet使用
        requestDispatcher.forward(request, response);

    }

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

  1. 创建 ManageServlet.java
package com.xjs.servlet.request;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/2/27-02-27-21:19
 * @Version: 1.0
 */
public class ManageServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ManageServlet 被调用~~");

        String username = request.getParameter("username");
        String role = (String) request.getAttribute("role");

        //输出信息
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("用户名:" + username + "<br/>");
        writer.print("角 色:" + role);
        writer.flush();
        writer.close();

    }

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

  1. 完成测试:注意看浏览器的请求次数和数据通信情况
8.11.7.5 请求转发注意事项和细节
  1. 浏览器地址不会变化(地址会保留在第 1 个 servlet 的 url)
  2. 在同一次 HTTP 请求中,进行多次转发,仍然是一次 HTTP 请求
  3. 在同一次 HTTP 请求中,进行多次转发,多个 Servlet 可以共享 request 域/对象的数据(因为始终是同一个 request 对象)
  4. 可以转发到 WEB-INF 目录下 (后面做项目使用)
  5. 不能访问当前 WEB 工程外的资源

在这里插入图片描述


  1. 因为浏览器地址栏会停止在第一个 servlet ,如果你刷新页面,会再次发出请求(并且会带数据), 所以在支付页面情况下,不要使用请求转发,否则会造成重复支付

8.11.8 课后作业

  1. 下面是一个表单,前面我们使用过,完成如下功能

在这里插入图片描述


(1)编写一个 RegisterServlet,能够接收到提交的各种数据
(2)并把接收到的数据返回给浏览器,并显示(回显)
(3)注意处理中文乱码问题
(4)暂不处理文件提交

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>表单综合案例</title>
</head>
<body>
<form action="http://localhost:8080/servlet/registerServlet" method="post">
    用户注册信息<br/>
    用户名称: <input type="text" name="username"><br/>
    用户密码: <input type="password" name="pwd1"><br/>
    确认密码: <input type="password" name="pwd2"><br/>
    选择你喜欢的运动项目:
    <input type="checkbox" name="sport" value="篮球">篮球<br/>
    <input type="checkbox" name="sport" value="足球" checked>足球<br/>
    <input type="checkbox" name="sport" value="手球" checked>手球<br/>
    请选择性别 :
    <input type="radio" name="gender" value="male"><br/>
    <input type="radio" name="gender" value="female"><br/>
    请选择城市:
    <select name="city">
        <option>--选择--</option>
        <option value="cd">成都</option>
        <option value="bj">北京</option>
        <option value="sh">上海</option>
    </select><br/>
    自我介绍:
    <textarea name="info" rows="6" cols="20"></textarea><br/>
    选择你的文件(头像)<input type="file" name="myfile"><br/>
    <input type="submit" value="提交"/> <input type="reset" value="重置"/>
</form>
</body>
</html>

package com.xjs.servlet.request.homework;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/2/27-02-27-22:28
 * @Version: 1.0
 */
public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //接受用户提交的数据
        request.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        String pwd1 = request.getParameter("pwd1");
        String pwd2 = request.getParameter("pwd2");

        String[] sports = request.getParameterValues("sport");

        String gender = request.getParameter("gender");

        String[] cities = request.getParameterValues("city");

        String info = request.getParameter("info");

        //解决乱码的方式
        //解读:
        //1. 设置服务器使用utf-8
        //response.setCharacterEncoding("utf-8");
        //2. 设置浏览器端是utf-8, 而且类型是 text/html
        //response.setHeader("Content-Type", "text/html;charset=utf-8");

        //回显数据
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("用户名称:" + username + "<br/>");
        writer.print("用户密码:" + pwd1 + "<br/>");
        writer.print("确认密码:" + pwd2 + "<br/>");
        writer.print("请选择你喜欢的运动项目:<br/>");
        for (String sport : sports) {
            writer.print(sport);
        }
        writer.print("<br/>");
        writer.print("请选择性别:");
        writer.print(gender + "<br/>");
        writer.print("请选择城市:<br/>");
        for (String city : cities) {
            writer.print(city);
        }
        writer.print("<br/>");
        writer.print("自我介绍:<br/>");
        writer.print(info);

    }

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

在这里插入图片描述

  1. 请编写一个 Servlet, 可以获取到浏览器所在电脑的操作系统版本和位数(32 还是 64),显示在页面即可
package com.xjs.servlet.request.homework;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: 谢家升
 * @Date: 2022/2/28-02-28-7:20
 * @Version: 1.0
 */
public class ComputerServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ComputerServlet 被调用~~");

        //解读:
        // (1) Windows NT 10.0; Win64; x64; rv:97.0
        // (2) Windows NT 10.0 和 Win64
        // (3) 回顾使用正则表达式
        String userAgent = request.getHeader("User-Agent");
        //java基础 正则表达式
        String regStr = "\\((.*)\\)";
        Pattern compile = Pattern.compile(regStr);
        Matcher matcher = compile.matcher(userAgent);
        matcher.find();// 因为我们的userAgent只有一组 ()
        String group = matcher.group(0);// (Windows NT 10.0; Win64; x64; rv:97.0)
        String group1 = matcher.group(1);// Windows NT 10.0; Win64; x64; rv:97.0
        String[] operInfos = group1.split(";");
        System.out.println("操作系统=" + operInfos[0]);//Windows NT 10.0
        System.out.println("操作系统位数=" + operInfos[1].trim());// Win64


    }

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

8.12 HttpServletResponse

8.12.1 HttpServletResponse 介绍

  1. 每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给 Servlet 程序去使用
  2. HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,通过 HttpServletResponse

8.12.2 HttpServletResponse 类图

在这里插入图片描述

在这里插入图片描述

8.12.3 向客户端返回数据方法

在这里插入图片描述

  1. 字节流 getOutputStream(),常用于下载 (处理二进制数据)
  2. 字符流 getWriter() ,常用于回传字符串
  3. 细节:两个流同时只能使用一个,使用了字节流,就不能再使用字符流,反之亦然,否则就会报错

8.12.4 向客户端返回数据应用实例

  1. 需求:浏览器请求 , 返回 hello, world
  2. 代码实现
package com.xjs.servlet.response;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/3/1-03-01-9:53
 * @Version: 1.0
 */
public class ResponseServlet_ extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("ResponseServlet_ 被调用~~");

        //注意:
        //1. setContentType 会设置服务器和客户端都用 utf-8 字符集,还设置了响应头
        //2. setContentType 要在获取流对象(getWriter)之前调用才有效
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("<h1>hello,world~~</h1>");

    }

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

  1. 在 web.xml 中完成配置
<!--配置ResponseServlet_-->
    <servlet>
        <servlet-name>ResponseServlet_</servlet-name>
        <servlet-class>com.xjs.servlet.response.ResponseServlet_</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ResponseServlet_</servlet-name>
        <url-pattern>/responseServlet_</url-pattern>
    </servlet-mapping>
  1. 完成测试

在这里插入图片描述

8.12.5 向客户端返回数据注意事项和细节

  1. 处理中文乱码问题-方案 1

在这里插入图片描述

//解决乱码的方式1
//解读:
//1. 设置服务器使用utf-8
response.setCharacterEncoding("utf-8");
//2. 设置浏览器端是utf-8, 而且类型是 text/html
response.setHeader("Content-Type", "text/html;charset=utf-8");
  1. 处理中文乱码问题-方案 2

在这里插入图片描述

//处理中文乱码问题-方式2:
//1. setContentType 会设置服务器和客户端都用 utf-8 字符集,还设置了响应头
//2. setContentType 要在获取流对象(getWriter)之前调用才有效
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();

8.12.6 请求重定向

8.12.6.1 请求重定向介绍
  1. 请求重定向指:一个 web 资源收到客户端请求后,通知客户端去访问另外一个 web资源,这称之为请求重定向
  2. 请求重定向原理示意图

在这里插入图片描述

8.12.6.2 请求重定向应用实例
  1. 需求 : 演示请求重定向的使用,当访问DownServlet 下载 文件 , 重定向到DownServletNew 下载文件

在这里插入图片描述

  1. 创建 DownServlet.java
package com.xjs.servlet.response;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/2/28-02-28-9:15
 * @Version: 1.0
 */
public class DownServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //System.out.println("DownServlet 被调用~~");
        //response.setContentType("application/x-tar;charset=utf-8");
        //PrintWriter writer = response.getWriter();
        //writer.print("hi");
        //writer.flush();
        //writer.close();

        //完成自己的业务
        //发出请求重定向 ==> DownServletNew
        //解读:
        //(1) sendRedirect 本质就会 返回 302 状态码 以及 Location: /servlet/downServletNew
        //(2) 因此302 和 /servlet/downServletNew 是浏览器解析,而不是服务器
        //(3) 浏览器在解析 /servlet/downServletNew => http://localhost:8080/servlet/downServletNew
        //(4) 动态获取 Application context
        String contextPath = getServletContext().getContextPath();
        System.out.println("contextPath= " + contextPath);//contextPath= /servlet
        response.sendRedirect(contextPath + "/downServletNew");
        //response.sendRedirect("http://www.baidu.com");

        //第二种重定向的写法
        //System.out.println("第2种重定向方式~~~");
        //response.setStatus(302);//设置http响应状态码
        设置http响应的 Location: /servlet/downServletNew
        //response.setHeader("Location","/servlet/downServletNew");


    }

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

  1. 创建 DownServletNew.java
package com.xjs.servlet.response;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 谢家升
 * @Date: 2022/2/28-02-28-9:17
 * @Version: 1.0
 */
public class DownServletNew extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DownServletNew 被调用~~");

        System.out.println("DownServlet 被调用~~");
        response.setContentType("application/x-tar;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("hi");
        writer.flush();
        writer.close();

    }

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

  1. 在 web.xml 中完成 servlet 配置
<servlet>
        <servlet-name>DownServlet</servlet-name>
        <servlet-class>com.xjs.servlet.response.DownServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DownServlet</servlet-name>
        <url-pattern>/downServlet</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>DownServletNew</servlet-name>
        <servlet-class>com.xjs.servlet.response.DownServletNew</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DownServletNew</servlet-name>
        <url-pattern>/downServletNew</url-pattern>
    </servlet-mapping>
  1. 完成测试
8.12.6.3 请求重定向注意事项和细节
  1. 最佳应用场景:网站迁移,比如原域名是 www.xjs.com 迁移到 www.xjs.cn ,但是百度抓取的还是原来网址
  2. 浏览器地址会发生变化,本质是两次 http 请求
  3. 不能共享 Request 域中的数据,本质是两次 http 请求,会生成两个 HttpServletRequest对象
  4. 不能重定向到 /WEB-INF 下的资源
  5. 可以重定向到 Web 工程以外的资源, 比如到 www.baidu.com
  6. 重定向有两种方式,推荐使用第 1 种
  • 第1种重定向方式【推荐】

在这里插入图片描述

		//完成自己的业务
        //发出请求重定向 ==> DownServletNew
        //解读:
        //(1) sendRedirect 本质就会 返回 302 状态码 以及 Location: /servlet/downServletNew
        //(2) 因此302 和 /servlet/downServletNew 是浏览器解析,而不是服务器
        //(3) 浏览器在解析 /servlet/downServletNew => http://localhost:8080/servlet/downServletNew
        //(4) 动态获取 Application context
        String contextPath = getServletContext().getContextPath();
        System.out.println("contextPath= " + contextPath);//contextPath= /servlet
        response.sendRedirect(contextPath + "/downServletNew");
        //response.sendRedirect("http://www.baidu.com");
  • 第2种重定向方式

在这里插入图片描述

		//第二种重定向的写法
        //System.out.println("第2种重定向方式~~~");
        response.setStatus(302);//设置http响应状态码
        //设置http响应的 Location: /servlet/downServletNew
        response.setHeader("Location","/servlet/downServletNew");
  1. 动态获取到 application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath= " + contextPath);// /servlet
response.sendRedirect(contextPath + "/downServletNew");

8.12.7 课后作业

  1. 具体需求

在这里插入图片描述


在这里插入图片描述


  • 编写一个 MyPayServlet , 能够接收到提交的数据
  • 编写一个简单的支付页面 pay.html(如图)
  • 如果支付金额大于 100,则重定向到 payok.html,否则重定向到原来的 pay.html

【pay.html】

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付页面</title>
</head>
<body>
<h1>支付页面</h1>
<!--注意:这里action="/servlet/myPayServlet"的第一/被浏览器解析从浏览器地址栏的主机-->
<form action="/servlet/myPayServlet" method="post">
    用户编号: <input type="text" name="userid"><br/>
    支付金额: <input type="text" name="money"><br/><br/>
    <input type="submit" value="点击支付">
</form>
</body>
</html>

【payOk.html】

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付成功</title>
</head>
<body>
<h1>恭喜你,支付成功~</h1>
</body>
</html>

package com.xjs.servlet.response.homework;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/2/28-02-28-18:25
 * @Version: 1.0
 */
public class MyPayServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("myPayServlet 被调用~~~");

        //获取表单提交的数据
        String userid = request.getParameter("userid");
        String money = request.getParameter("money");

        String contextPath = getServletContext().getContextPath();

        //int moneyCount = Integer.parseInt(money);
        //处理一下这个异常
        int i = WebUtils.parseStr(money);

        if (i > 100) {
            //重定向到 payOk.html
            response.sendRedirect(contextPath + "/payOk.html");
        } else {
            //重定向到 pay.html
            response.sendRedirect(contextPath + "/pay.html");
        }

    }

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

【WebUtils.java】

package com.xjs.servlet.response.homework;

/**
 * @Author: 谢家升
 * @Date: 2022/2/28-02-28-20:18
 * @Version: 1.0
 */
public class WebUtils {

    public static int parseStr(String str) {

        int num = 0;
        try {
            num = Integer.parseInt(str);
        } catch (NumberFormatException e) {
            //这个异常不会throw 给 tomcat
            System.out.println("你输入的str格式不正确...");
        }
        return num;

    }

}

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐