Servlet

大家好呀,我是小笙,我和大家分享下我学习Javaweb的笔记

Servlet

概述

servlet在线文档

概述:Servlet(Server Applet),全称Java Servlet。是用Java编写的服务器端程序,其主要功能在于交互式地浏览和修改数据,生成动态Web内容

Servlet的特点

  • 浏览器的多次请求Servlet资源 ,通常情况下,服务器只会创建一个 Servlet 实例对象, 也就是说 Servlet 实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至 web 容器退出或者 redeploy web 应用,servlet 实例对象才会销毁

  • 是由服务器调用和执行的Java 程序 ,不能独立运行

  • 按照Servlet规范开发的(实现了Servlet接口)

  • 在Servlet 的整个生命周期内,init 方法只被调用一次。而对每次请求都调用一次service 方法

Servlet 基本使用

servlet3.0 前使用 web.xml , servlet3.0 版本以后(包括 3.0)支持注解, 同时支持 web.xml 配置

浏览器请求Servlet资源流程

image-20220424165555740

实例代码

<?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-->
        <!--
                servlet-class: Servlet所在的全类名路径
                url-pattern: 浏览器访问该Servlet的路径
                servlet-name: 启到桥梁的作用,名字可以自己定义,但是必须唯一
        -->
        <servlet>
                <servlet-name>helloServlet</servlet-name>
                <servlet-class>com.al_tair.servlet.HelloServlet</servlet-class>
        </servlet>
        <servlet-mapping>
                <servlet-name>helloServlet</servlet-name>
                <url-pattern>/helloServlet</url-pattern>
        </servlet-mapping>
</web-app>
/**
 * 开发一个Servlet需要实现该接口
 */
public class HelloServlet implements Servlet {
    /**
     * 初始化Servlet
     * 当Tomcat创建HelloServlet示例时,会调用init方法
     * 该方法只能被嗲用一次
     *
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    }

    /**
     * 返回Servlet的配置
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * Service方法 处理浏览器的请求(get或者post)
     * 当浏览器每次请求Servlet时候,就会调用该方法
     * 当tomcat调用该方法时,会把http请求的数据封装成实现了ServletRequest接口的request对象
     * @param servletRequest 通过servletRequest获取用户提交的数据
     * @param servletResponse 用于返回数据给tomcat,再返回给浏览器
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

    /**
     * 返回Servlet的信息
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 该方法是在servlet销毁的时候的调用
     * 只能被调用一次
     */
    @Override
    public void destroy() {

    }
}

Servlet 生命周期

示意图

image-20220424174145856

init()初始化阶段

Servlet 容器(比如: Tomcat)加载 Servlet,加载完成后,Servlet 容器会创建一个 Servlet 实例 并调用 init()方法,init()方法只会调用一次,

Tomcat服务器在下面的情况装载 Servlet实例:

  • Servlet 容器(Tomcat)启动时自动装载某些 servlet,实现这个需要在 web.xml 文件中添加 1

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.al_tair.servlet.HelloServlet</servlet-class>
        <!-- 1 表示装载的顺序,默认为-1不会自动装载 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/helloServlet</url-pattern>
    </servlet-mapping>
    
  • 在 Servlet 容器(Tomcat)启动后,浏览器首次向 Servlet资源发送http请求

service()处理浏览器请求阶段

  • Tomcat每收到一个http请求,将会产生一个新的线程去处理servlet实例

  • 创建一个用于封装http请求消息的ServletRequest对象和一个代表http响应消息的ServletResponse对象

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

destroy()终止阶段

当Servlet 容器终止运行,或者Servlet类重新装载时,会调用destroy() 方法

HttpServlet

框架图

image-20220425165456913

代码示例

<?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">
        <servlet>
                <servlet-name>hiHttpServlet</servlet-name>
                <servlet-class>com.al_tair.servlet.HiHttpServlet</servlet-class>
        </servlet>
        <servlet-mapping>
                <servlet-name>hiHttpServlet</servlet-name>
                <url-pattern>/hiHttpServlet</url-pattern>
        </servlet-mapping>
</web-app>
public class HiHttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HttpServletRequest doGet");
    }

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

// 源码分析到底是如何调用doGet和doPost方法
// HttpServlet类重写了Servlet接口中的Service方法
// 本质还是调用Service方法,只不过底层封装了该方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            // 根据动态绑定机制,会调用HiHttpServlet类中的doGet方法
            this.doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader("If-Modified-Since");
            } catch (IllegalArgumentException var9) {
                ifModifiedSince = -1L;
            }

            if (ifModifiedSince < lastModified / 1000L * 1000L) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }
}
// 如果我们不重写doGet和doPost方法,就会去调用HttpServlet类中的这两个方法,显示效果如下
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_put_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(405, msg);
    } else {
        resp.sendError(400, msg);
    }
}

image-20220425171505753

Servlet 注解方式

@WebServlet

示例代码

// 可以配置多个访问地址urlPatterns = {"/annotationServlet","/annotationServlet2"}
// urlPatterns 对应web.xml文件中的url-pattern
@WebServlet(urlPatterns = {"/annotationServlet"})
public class AnnotationServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

// @WebServlet注解源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
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 "";
}
        
/**
 * 可以通过配置web.xml找到该Servlet,同样也可以通过注解来实现
 * 模拟Tomcat通过反射来得到注解的数据,进而匹配Servlet
 */
public class TestAnnotation {
    public static void main(String[] args) throws ClassNotFoundException {
        // 首先通过扫描包的形式,得到Servlet类的全路径
        String filePath = "com.al_tair.servlet.annotation.AnnotationServlet";

        // 反射得到注解并获取注解中的urlPatterns的值
        Class cls = Class.forName(filePath);
        WebServlet webServlet = (WebServlet)cls.getAnnotation(WebServlet.class);
        String[] urls = webServlet.urlPatterns();

        // 遍历urlPatterns的值,与http请求资源路径进行匹配
        for (String url:urls
             ) {
            System.out.println(url); /// annotationServlet
        }
    }
}

urlPattern 配置

精准匹配

@WebServlet(urlPatterns = {"/annotationServlet"})

目录匹配

// /annotationServlet/后添加任何字母都可以访问到
@WebServlet(urlPatterns = {"/annotationServlet/*"})

扩展名匹配

// 以annotationServlet为后缀的可以匹配
@WebServlet(urlPatterns = {"*.annotationServlet"})

任意匹配

// 任意匹配
@WebServlet(urlPatterns = {"/*"})
@WebServlet(urlPatterns = {"/"})
// 当Servlet配置了"/"或者"/*", 会覆盖tomcat的DefaultServlet,当其他的utl-pattern都匹配不上时,都会走DefaultServlet,一旦拦截,静态资源不能处理
<!-- DefaultServlet 在Tomcat/conf/web.xml中配置 -->
<!-- The default servlet for all web applications, that serves static resources -->
<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>

优先级

精准匹配 > 目录匹配 > 扩展名匹配 > 任意匹配 > /* > / > 默认匹配(DefaultServlet)

ServletConfig

ServletConfig类是为Servlet程序配置信息的类,ServletConfig对象由Tomcat创建

image-20220427154315590

// GenericServlet父类
// 如果该init方法没有被重写,就会默认调用父类GenericServlet的该方法
// 这个时候Tomcat在初始化的时候传入了ServletConfig对象
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

测试代码

假设我们要读取配置文件中的数据库数据

<?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">
        <servlet>
            <servlet-name>DBServlet</servlet-name>
            <servlet-class>com.al_tair.servletConfig.DBServlet</servlet-class>
            <!--配置连接数据库的用户名和密码-->
            <init-param>
                <param-name>username</param-name>
                <param-value>root</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>/dbServlet</url-pattern>
        </servlet-mapping>

</web-app>
public class DBServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String username = servletConfig.getInitParameter("username");
        System.out.println(username);
        String pwd = servletConfig.getInitParameter("pwd");
        System.out.println(pwd);
    }
}

// getServletConfig()源码分析
// 初始化的时候默认传入了ServletConfig对象,并让属性config获取该对象地址的引用
// 然后通过get方法供子类调用获取该配置对象进而获取配置对象的数据
private transient ServletConfig config;
public ServletConfig getServletConfig() {
    return this.config;
}

ServletContext

基本介绍

  • ServletContext是一个接口,用来解决Servlet对象之间的数据交互,因此也被称作域对象

    image-20220427165315588

  • 一个web工程,只有一个 ServletContext 对象实例,ServletContext 对象是在 web 工程启动的时候创建,在 web 工程停止的时销毁

  • ServletContext 对象可以通过 ServletConfig.getServletContext 方法获得对 ServletContext 对象的引用

    // 父类GenericServlet有该对象的引用
    // 因此也可以通过 this.getServletContext()来获得其对象的引用
    public ServletContext getServletContext() {
        return this.getServletConfig().getServletContext();
    }
    

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

  • 获取 web.xml 中配置的上下文参数 context-param
  • 获取当前的工程路径,格式: /工程路径
  • 获取工程部署后在服务器硬盘上的绝对路径
<?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">
        <!--ServletContext-->
        <servlet>
            <servlet-name>ServletContextDemo</servlet-name>
            <servlet-class>com.al_tair.servletContext.ServletContextDemo</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>ServletContextDemo</servlet-name>
            <url-pattern>/servletContext</url-pattern>
        </servlet-mapping>
        <!-- 配置整个网站的信息 -->
        <context-param>
            <param-name>website</param-name>
            <param-value>https://github.com/steven-beep</param-value>
        </context-param>
        <context-param>
            <param-name>school</param-name>
            <param-value>XXXXXXX</param-value>
        </context-param>
</web-app>
public class ServletContextDemo extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        // 1.获取 web.xml 中配置的上下文参数 context-param
        String website = servletContext.getInitParameter("website");
        String school = servletContext.getInitParameter("school");
        System.out.println(website);
        System.out.println(school);

        // 2.获取当前的工程路径,格式: /工程路径
        String contextPath = servletContext.getContextPath();
        System.out.println(contextPath);

        // 3.获取工程部署后在服务器硬盘上的绝对路径
        String realPath = servletContext.getRealPath("/");
        System.out.println(realPath);
    }
}

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

image-20220427194039331

image-20220427194042960

<?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">
        <!-- 两个Servlet -->
        <servlet>
            <servlet-name>Servlet1</servlet-name>
            <servlet-class>com.al_tair.servletContext.Servlet1</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>Servlet1</servlet-name>
            <url-pattern>/servlet1</url-pattern>
        </servlet-mapping>
        <servlet>
            <servlet-name>Servlet2</servlet-name>
            <servlet-class>com.al_tair.servletContext.Servlet2</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>Servlet2</servlet-name>
            <url-pattern>/servlet2</url-pattern>
        </servlet-mapping>
</web-app>

// Servlet1 和 Servlet2的代码相同
public class Servlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = getServletContext();
        Integer count = (Integer) servletContext.getAttribute("count");
        if(count == null){
            servletContext.setAttribute("count",1);
            count = 1;
        }else{
            servletContext.setAttribute("count",++count);
        }
        System.out.println("Servlet1访问,共访问次数: " + count);

        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write("<h1>共访问次数: "+ count + "</h1>");
        writer.flush();
        writer.close();
    }
}

HttpServletRequest

当客户端/浏览器通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这个对象

image-20220427195449563

常用方法

测试代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <form action="http://localhost:9999/servlet/requestMethod" method="get">
        <div align="center">
            <h1 align="center">表单</h1>
            用户名<input type="text" name="username"/><br>
            密 码<input type="password" name="pwd"/><br>
            <input type="checkbox" name="hobby" value="zq">足球
            <input type="checkbox" name="hobby" value="lq">篮球
            <input type="checkbox" name="hobby" value="pq">排球<br/>
            <button type="submit">提交表单</button>
        </div>
    </form>
</body>
</html>
@WebServlet(urlPatterns = {"/requestMethod"})
public class RequestMethod extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // getRequestURI() 获取请求的资源路径
        String requestURI = req.getRequestURI();
        System.out.println(requestURI); // /servlet/requestMethod

        // getRequestURL()获取请求的统一资源定位符(绝对路径)
        StringBuffer requestURL = req.getRequestURL();
        System.out.println(requestURL); // http://localhost:9999/servlet/requestMethod

        // getRemoteHost()获取客户端/浏览器的主机
        String remoteHost = req.getRemoteHost();
        System.out.println(remoteHost ); // 127.0.0.1

        // getHeader()获取请求头
        String header = req.getHeader("Accept");
        // text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,
        // image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
        System.out.println(header);

        // getParameter()获取请求的参数
        // 注意解码url编码,不会会出现中文乱码(并且要写在接受数据前)
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        String pwd = req.getParameter("pwd");
        System.out.println(username); // Al_tair
        System.out.println(pwd); // 123456

        // getParameterValues()获取请求的参数(多个值的时候使用), 比如checkbox,返回数组
        String[] hobbies = req.getParameterValues("hobby");
        for (String hobby: hobbies
             ) {
            System.out.println(hobby); // zq lq
        }

        // getMethod()获取请求的参数
        System.out.println(req.getMethod()); // GET

        // setAttribute(key, value)设置域数据
        req.setAttribute("count",2);

        // getAttribute(key)获取域数据
        Integer count = (Integer)req.getAttribute("count");
        System.out.println(count); // 2
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
}

请求转发

概述:请求转发指一个 web 资源收到客户端请求后,通知服务器去调用另外一个 web 资源进行处理

  • HttpServletRequest 对象提供了一个 getRequestDispatcher 方法,该方法返回一个 RequestDispatcher 对象,调用这个对象的 forward 方法可以实现请求转发
  • HttpServletRequest 对象是一个域对象(共享对象),可以通过 HttpServletRequest 对象在实现转发时,把数据通过 HttpServletRequest 对象带给其它 web 资源处理,可以使用如下方法
    • setAttribute方法
    • getAttribute方法
    • removeAttribute方法
    • getAttributeNames方法
请求转发原理

image-20220428131521640

示例代码

image-20220428151211405

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:9999/servlet/checkServlet" method="get">
        <h1>检查是否该用户为管理员</h1>
        用户名<input type="text" name="username"><br/>
        <button type="submit">按钮</button>
    </form>
</body>
</html>
<?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">
        <!-- 请求转发 -->
        <servlet>
            <servlet-name>CheckServlet</servlet-name>
            <servlet-class>com.al_tair.HttpServletRequest.CheckServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>CheckServlet</servlet-name>
            <url-pattern>/checkServlet</url-pattern>
        </servlet-mapping>
        <servlet>
            <servlet-name>ManageServlet</servlet-name>
            <servlet-class>com.al_tair.HttpServletRequest.ManageServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>ManageServlet</servlet-name>
            <url-pattern>/manageServlet</url-pattern>
        </servlet-mapping>
</web-app>

image-20220428151429587

// CheckServlet
public class CheckServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        if(username.equals("tom")){
            request.setAttribute("role", "管理员");
        }else{
            request.setAttribute("role", "普通用户");
        }
        // 获取分发器
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/manageServlet");
        // 表示把当前CheckServlet的request对象和response对象,传递给ManageServlet使用
        requestDispatcher.forward(request, response);
    }
}

// ManageServlet
public class ManageServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        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();
    }
}
注意事项
  • 浏览器访问资源地址不会变化(都是第一个资源的url)
    • 因为浏览器地址栏会停止在第一个资源地址,如果你刷新页面,会再次发出请求(并且会带数据), 所以在支付页面的时候,不要使用请求转发,否则会造成重复支付的问题
  • 在同一次 HTTP 请求中,进行多次转发,多个 Servlet 可以共享 request 域/对象的数据(因为始终是同一个 request 对象)
  • 可以转发到 WEB-INF 目录下的资源(该目录资源无法通过浏览器直接访问)
  • 不能访问当前 WEB 工程外的资源

HttpServletResponse

概念:每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给Servlet

作用:主要用于响应浏览器,返回数据给浏览器

image-20220428172925090

常用方法

// 父接口ServletResponse的方法
// 字节流,常用于下载文件
ServletOutputStream getOutputStream() throws IOException;
// 字符流,常用于传输字符串
PrintWriter getWriter() throws IOException;
// 注意:字节流和字符流在同一个时间段只能使用其中一个

// 用于处理中文乱码(本质没有区别)
// 方法一
response.setCharacterEncoding("utf-8"); // 设置服务器字符集为utf-8
response.setHeader("ContentType","text/html;charset=utf-8"); // 设置浏览器使用该字符集
// 方法二
response.setContentType("text/html;charset=utf-8");

请求重定向

概念:一个 web 资源收到客户端请求后,通知客户端去访问另外一个 web 资源

image-20220428185228710

重定向的两种方式

// 方式1
response.sendRedirect("/servlet/xxServlet");

// 方式2
response.setStatus(302);
response.setHeader("Location","/servlet/xxServlet");

示例代码

  • 编写一个 MyPayServlet , 能够接收到提交的数据

  • 编写一个简单的支付页面 pay.html

    image-20220428200852063

  • 如果支付金额大于 100, 则重定向到 payok.html, 否则重定向到原来的 pay.html

    image-20220428200830414

<!-- pay.html -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>支付页面</title></head>
    <body>
    <h1>支付页面</h1> <!--注意:这里 action="/servlet/myPayServlet"的第一/被浏览器解析从浏览器地址栏的 主机-->
    <form action="/homework/myPayServlet" method="get">
        用户编号: <input type="text" name="userid"><br/>
        支付金额: <input type="text" name="money"><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>
@WebServlet(name = "HttpServletResponse",urlPatterns = {"/myPayServlet"})
public class myPayServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
    protected void doGet(HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException {
        String money = request.getParameter("money");
        String contextPath = getServletContext().getContextPath();
        if(Integer.parseInt(money) > 100){
            response.sendRedirect(contextPath + "/payok.html");
        }else{
            response.sendRedirect(contextPath + "/pay.html");
        }
    }
}

注意细节

  • 最佳应用场景:网站迁移,比如原域名是 XXX.com 迁移到 XXX.cn ,但是百度抓取的还是原来网址

  • 浏览器地址会发生变化,不能共享 Request 域中的数据,本质是两次 http 请求

  • 不能重定向到 /WEB-INF 下的资源

  • 可以重定向到 Web 工程以外的资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

罗念笙

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

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

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

打赏作者

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

抵扣说明:

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

余额充值