3.Servlet

3.Servlet

1.Servlet的生命周期

Servlet的生命周期
Servlet的生命周期

package com.example.demo;/**
 * @Author:zhoayu
 * @Date:2023/10/25 22:30
 * @Description:com.example.demo
 * @version:1.0
 */

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

/**
 * @ClassName MyServlet
 * @Description //TODO 
 * @Author zhaoyu
 * @Date 2023/10/25
 */
public class MyServlet extends HttpServlet {
    //Servlet对象的生命周期
    
    //构造一个Servlet对戏那个
    public MyServlet(){
        System.out.println("MyServlet Constructor invoked");
    }

    //初始化
    @Override
    public void init() throws ServletException {
        System.out.println("init method invoked");
    }

    //执行服务
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("service method invoked");
    }

    //销毁对象
    @Override
    public void destroy() {
        System.out.println("destroy method invoked");
    }
}
 

当我们第一次请求一个servlet资源的时候,对应的servlet对象会被tomcat创建,service方法会被调用。
当我们后续再次请求这个servlet资源的时候,直接用上次创建的servlet对象再次调用service。
即Servlet对象创建1次(第一次请求servlet资源时完成),初始化1次(第一次请求servlet资源时完成),执行服务多次(每次请求servlet资源时完成),销毁1次(tomcat停止时完成)

Servlet对象的线程安全问题
一个Servlet对象只会被创建一次,假设我在servlet对象中定义了一个成员变量int i = 10,在service方法中定义 System.out.println(i++);
每次请求i都会变化,eg:第一次请求:10,第二次请求:11,第三次请求:12…
这样可能会有并发安全问题,当多个请求并发请求servlet时,由于i++操作不是原子性操作,可能就会有线程安全问题,对于这种单例的对象在访问时我们一定要考虑到线程安全问题
解决上述线程安全问题的两个方法:1.给i加volatile关键字 2.给service方法加synchronized关键字。这二者都能解决有序性和可见性的问题,其中volatile解决有序性是通过指令屏障的方式,synchronized解决有序性是因为单线程的指令重排序对结果是没影响的。
我们同时需要注意,在servlet中尽量避免使用成员变量,如果使用的话,应该尽量避免修改成员变量,只读的话一定是安全的。如果要修改单例的对象or变量,应该注意线程安全问题。

加快第一次请求时的响应
如果在执行构造方法/初始化Servlet对象的时候逻辑很重,第一次访问这个Servlet对象的时候创建Servlet对象等了很久(可以在constructor或者init()中加一个Thread.sleep(5000)来模拟),那么用户的页面就会一直卡住,体验很差。
如果我们能让Tomcat一启动的时候就把这个Servlet对象创建好,我们在请求的时候就不用现new对象了。我们可以通过在web.xml中加<load-on-startup>n(该对象的初始化顺序,若干个对象的n都相同时,servlet会随机初始化)</load-on-startup>来实现。此时我们就把这个耗时从用户第一次请求等待返回转移到了tomcat启动,保证了用户体验。
ps:因为tomcat在启动的时候会初始化一些Servlet对象(比如JspServlet,DefaultServlet…),具体可以在tomcat的配置文件web.xml中搜索load-on-startup标签找到,它们已经被依次定义为了第1、2、3、4、5个初始化,所以我们的自定义Servlet可以从6开始写。

    <servlet>
        <!--Servlet子实现类的别名-->
        <servlet-name>MyServlet</servlet-name>
        <!--对应的Servlet子实现类的相对路径-->
        <servlet-class>com.example.demo.MyServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet.do</url-pattern>
    </servlet-mapping>

2.ServletContext对象

ServletContext对象是我们javaweb项目中最大的一个域对象
ServletContext对象介绍:Servlet上下文对象,
1.可以为所有的Servelt提供初始化数据,被同一个webapp下的所有Servlet共享。
2.ServletContext可以帮助我们实现数据的传递(eg:Servlet1和Servlet2之间想要传递数据)
3.对同一个webapp来说,ServletContext对象是单例的
ServletContext对象介绍
ServletContext对象的作用:

  • 相对路径转绝对路径
  • 读取容器的附加信息
  • 读取配置信息
  • 全局容器

ServletContext对象为所有Servlet实例提供初始化数据:
将数据以key-value的形式设置在ServletContext对象中,所有的Servlet实例都可以通过创建这个ServletContext对象后使用,在web.xml中进行配置:

    <context-param>
        <param-name>username</param-name>>
        <param-value>zhaoyu</param-value>>
    </context-param>
    <context-param>
        <param-name>password</param-name>>
        <param-value>12345678</param-value>>
    </context-param>
        //获取web.xml中给ServletContext对象配置的全局初始信息
        String username = servletContext.getInitParameter("username");//zhaoyu
        String password = servletContext.getInitParameter("password");//12345678

ServletContext对象的一些api使用和功能体现:

package com.example.demo;/**
 * @Author:zhoayu
 * @Date:2023/10/28 21:49
 * @Description:com.example.demo
 * @version:1.0
 */

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.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;

/**
 * @ClassName ServletContextText
 * @Description //TODO 
 * @Author zhaoyu
 * @Date 2023/10/28
 */
public class ServletContextText extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取Servlet对象的方式
        //1.通过req对象
        ServletContext servletContext = req.getServletContext();
        //2.通过this.getServletContext() 继承自HttpServlet
        ServletContext servletContext1 = this.getServletContext();
        //验证每次获得的servletContext对象是不是同一个(即一个webapp中ServletContext对象是不是单例的)
        System.out.println(servletContext==servletContext1); // true

        //ServletContext对象作用演示:
        //1.获取当前项目的部署名
        String contextPath = servletContext.getContextPath(); // /xxx.war_exploded(application context名)
        //2.将一个相对路径(参数)转换为项目的绝对路径
        String fileUpload = servletContext.getRealPath("fileUpload");// /Users/zhaoyu/ServletProject/out/artifacts/xxx.war_exploded/fileUpload
        //3.获取servlet container的信息
        String serverInfo = servletContext.getServerInfo(); // Tomcat 9.0.80
        //4.获取Servlet API的主版本号和小版本号
        int majorVersion = servletContext.getMajorVersion(); // 4
        int minorVersion = servletContext.getMinorVersion(); // 0

        //获取web.xml中给ServletContext对象配置的全局初始信息
        String username = servletContext.getInitParameter("username");//zhaoyu
        String password = servletContext.getInitParameter("password");//12345678


        //这个Servlet实例向ServletContext对象中设置一些kv对,给下面这个Servlet实例读,以此实现Servlet对象之间的通信
        //域对象功能的体现
        //ps:需要请求触发这个Servlet对象,触发service方法执行这段设置kv对的逻辑,才能设置成功
        servletContext.setAttribute("obj",new Object()); // value可以是任何对象
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张三","李四","王五");
        servletContext.setAttribute("list",list);

    }
}

class ServletContextText2 extends HttpServlet{
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();

        //获取web.xml中给ServletContext对象配置的全局初始信息 同样能够获取到 它和ServletContextText中获取到的ServletContext对象其实也是同一个
        String username = servletContext.getInitParameter("username");//zhaoyu
        String password = servletContext.getInitParameter("password");//12345678

        //如果不知道web.xml中给context-param定义的name
        //获取web.xml中定义的所有的context-param(kv对)的keys
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        while(initParameterNames.hasMoreElements()){
            //遍历keys获得每个key对应的value
            String key = initParameterNames.nextElement();
            String value = servletContext.getInitParameter(key);
        }

        //获取上一个Servlet实例设置的kv对
        ArrayList<String> list = (ArrayList<String>) servletContext.getAttribute("list");
        for (String value : list){
            System.out.println(list);// 张三 李四 王五
        }

    }
}

3.Servletconfig对象

ServletConfig对象对应web.xml中的节点。当Tomcat初始化一个Servlet时,会将该Servelt的配置信息封装在一个ServletConfig对象中。我们可以通过该对象读取节点中的配置信息。即每个Servlet实例都会独有其自己的ServletConfig对象。
在web.xml中配置ServletConfigTest(HttpServlet子实现类)的参数init-param,这个参数就会被封装在ServletConfig对象中。

    <servlet>
        <servlet-name>ServletConfigTest</servlet-name>
        <servlet-class>com.example.demo.ServletConfigTest</servlet-class>
        <init-param>
            <param-name>company</param-name>
            <param-value>mt</param-value>
        </init-param>
        <init-param>
            <param-name>name</param-name>
            <param-value>zy</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletConfigTest</servlet-name>
        <url-pattern>/ServletConfigTest.do</url-pattern>
    </servlet-mapping>
package com.example.demo;/**
 * @Author:zhoayu
 * @Date:2023/10/29 22:59
 * @Description:com.example.demo
 * @version:1.0
 */

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;

/**
 * @ClassName ServletConfigTest
 * @Description //TODO 
 * @Author zhaoyu
 * @Date 2023/10/29
 */
public class ServletConfigTest extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获得ServletConfig对象
        ServletConfig servletConfig = this.getServletConfig();
        String company = servletConfig.getInitParameter("company");
        String name = servletConfig.getInitParameter("name");
        System.out.println(company); // mt
        System.out.println(name); // zy
    }
}

ServletConfig对象不向ServletContext对象,没有类似setAttribute()的方法向其中设置键值对。ServletConfig对戏那个在实际开发中使用的频率不高,作为了解即可。

4.url pattern匹配规则

精确匹配

url路径必须精确匹配中指出的路径
url精确匹配
url-pattern精确匹配:

    <servlet>
        <servlet-name>urlServlet</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <url-pattern>/urlServlet.do</url-pattern>
    </servlet-mapping>
扩展名匹配

在中允许使用通配符"“作为匹配规则,”“表示匹配任意字符。在扩展名匹配中只要扩展名相同都会被匹配,写多少层都无所谓,和路径无关。注意:在使用扩展名匹配时在中不能使用”/",否则容器就会抛出异常。
在这里插入图片描述
url扩展名匹配

    <servlet>
        <servlet-name>urlServlet</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
路径匹配

根据请求路径进行匹配,在请求路径中只要包含该路径都能匹配成功。"*"表示任意路径以及子路径
路径匹配
url路径匹配

    <servlet>
        <servlet-name>urlServlet</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <!--以/lujingpipei/suibian开头的任意url都能请求到urlTestServlet-->
        <url-pattern>/lujingpipei/suibian/*</url-pattern>
    </servlet-mapping>
任意匹配

匹配"/",只要请求当前项目下的路径,匹配所有但不包含JSP页面。
任意匹配
url任意匹配

    <servlet>
        <servlet-name>urlServlet</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <!--包含所有但是除了jsp页面-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>urlServlet</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <!--任意匹配且包含jsp页面 一般不推荐-->
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
优先顺序

总结就一条规则:哪个servlet的url-pattern更精确就走哪个servlet
优先顺序
URL映射方式
在web.xml文件中支持多个URL映射到同一个Servlet中,但是相同的URL不能同时映射到两个Servlet中。类似函数,多对一行,一对多不行。
方式1:多个URL映射到同一个Servlet中

    <servlet>
        <servlet-name>urlServlet</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <!--abc,bcd,cde,def路径都能请求到urlTestServlet-->
        <url-pattern>/abc</url-pattern>
        <url-pattern>/bcd</url-pattern>
        <url-pattern>/cde</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <!--再写一个servlet-mapping用不同的url映射到相同的Servlet也是可以的-->
        <url-pattern>/def</url-pattern>
    </servlet-mapping>

方式2:一个URL映射到多个Servlet中

    <servlet>
        <servlet-name>urlServlet</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet</servlet-name>
        <url-pattern>/abc</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>urlServlet2</servlet-name>
        <servlet-class>com.example.demo.urlTestServlet2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlServlet2</servlet-name>
        <url-pattern>/abc</url-pattern>
    </servlet-mapping>

5.注解模式开发Servlet

在Servlet3.0以及之后的版本中支持注解式开发servlet。对于Servlet的配置不再依赖于web.xml配置文件,而是使用@WebServlet将一个继承自javax.servlet.http.HttpServlet的类定义为Servlet组件。
@WebServlet

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2017-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.servlet.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Documented;

/**
 * Annotation used to declare a servlet.
 *
 * <p>This annotation is processed by the container at deployment time,
 * and the corresponding servlet made available at the specified URL
 * patterns.
 * 
 * @see javax.servlet.Servlet
 *
 * @since Servlet 3.0
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
    
    /**
     * The name of the servlet
     *
     * @return the name of the servlet
     */
    String name() default "";
    
    /**
     * The URL patterns of the servlet
     *
     * @return the URL patterns of the servlet
     */
    String[] value() default {};

    /**
     * The URL patterns of the servlet
     *
     * @return the URL patterns of the servlet
     */
    String[] urlPatterns() default {};
    
    /**
     * The load-on-startup order of the servlet 
     *
     * @return the load-on-startup order of the servlet
     */
    int loadOnStartup() default -1;
    
    /**
     * The init parameters of the servlet
     *
     * @return the init parameters of the servlet
     */
    WebInitParam [] initParams() default {};
    
    /**
     * Declares whether the servlet supports asynchronous operation mode.
     *
     * @return {@code true} if the servlet supports asynchronous operation mode
     * @see javax.servlet.ServletRequest#startAsync
     * @see javax.servlet.ServletRequest#startAsync(ServletRequest,
     * ServletResponse)
     */
    boolean asyncSupported() default false;
    
    /**
     * The small-icon of the servlet
     *
     * @return the small-icon of the servlet
     */
    String smallIcon() default "";

    /**
     * The large-icon of the servlet
     *
     * @return the large-icon of the servlet
     */
    String largeIcon() default "";

    /**
     * The description of the servlet
     *
     * @return the description of the servlet
     */
    String description() default "";

    /**
     * The display name of the servlet
     *
     * @return the display name of the servlet
     */
    String displayName() default "";

}

注解样例(了解即可) 后面我们会通过框架直接控制Controller,基本都不用自己定义Servlet了

package com.example.demo;/**
 * @Author:zhoayu
 * @Date:2023/10/31 22:05
 * @Description:com.example.demo
 * @version:1.0
 */

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;

/**
 * @ClassName ServletAnnoation
 * @Description //TODO 
 * @Author zhaoyu
 * @Date 2023/10/31
 */

//urlPatterns=等价于配置<serlvet-mapping>
//loadOnstartup = 6  指定这个servlet对象在tomcat启动时第6个被初始化,默认loadOnstartup是-1 即tomcat启动时不初始化
//initParams = {@WebInitParam{name="name",value="zy"},@WebInitParam(name="gender",value="man")} 等价于往这个Servelt对象对应的ServletConfig对象中设置kv对
@WebServlet(urlPatterns={"/servletAnnoation.do","/servletAnnoation2.do"},
        loadOnStartup = 6,
        initParams = {
        @WebInitParam(name="name",value="zy"),
        @WebInitParam(name="gender",value="man")
                    })
public class ServletAnnoation extends HttpServlet {
    public ServletAnnoation() {
        System.out.println("constructor invoked");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("init method invoked");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("service method invoked");
        ServletConfig servletConfig = this.getServletConfig();
        System.out.println(servletConfig.getInitParameter("name")); //zy
        System.out.println(servletConfig.getInitParameter("gender")); //man
    }
}

6.forward请求转发

请求转发和响应重定向

当Servlet处理不了当前请求,可以有两种方案:
1.请求转发(下图)请求转发又分为forward转发和include转发,请求转发是被请求的服务直接去请求别的服务,再把结果返回给浏览器。
请求转发例子
2.响应重定向:告诉浏览器自己去请求其他服务的资源,由浏览器自己去请求资源。
响应重定向
这两种方法也是后台控制页面跳转的主要的两种手段。

请求转发-forward(重要)

我们叫Servlet1为源组件,Servlet2叫目标组件。
请求转发-forward
Servlet1(源组件)

package com.example.demo;/**
 * @Author:zhoayu
 * @Date:2023/10/31 22:38
 * @Description:com.example.demo
 * @version:1.0
 */

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

/**
 * @ClassName forwardRequestTest
 * @Description //TODO 
 * @Author zhaoyu
 * @Date 2023/10/31
 */
@WebServlet(urlPatterns = "/forwardRequestTest.do")
//源组件
public class forwardRequestTest extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("forwardRequestTest invoked");
        //获取请求参数
        String money = req.getParameter("money");
        System.out.println("money:" + money);

        //请求转发给另一个Servlet(目标组件)
        //获得一个请求转发器 参数是目标组件Servlet的url-pattern
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("forwardRequestTest2.do");
        //由请求转发器做出转发动作(forward)
        //    public void forward(ServletRequest request, ServletResponse response)
        //        throws ServletException, IOException;
        requestDispatcher.forward(req,resp); //forwardRequestTest2的service方法也执行了
        //我们在forwardRequestTest2中处理请求,做出响应。等于完全托管给forwardRequestTest2了
        //最后由forwardRequestTest2做出响应

        //forwardRequestTest和forwardRequestTest2的req,resp对象在内存中就是同一个

        //做出响应
        PrintWriter writer = resp.getWriter();
        writer.println("servlet1 invoked");//这里其实把文字放到了resp对象中,在转发时源组件在resp对象中存储的数据会全部清掉
        //不会打印
        //Servlet1向resp中设置的任何数据,通过resp对象做出的任何响应,都不会响应给浏览器。都是无效的,响应这个动作完全托管给了Servlet2
    }
}

Servlet2(目标组件)

package com.example.demo;/**
 * @Author:zhoayu
 * @Date:2023/10/31 22:39
 * @Description:com.example.demo
 * @version:1.0
 */

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

/**
 * @ClassName forwardRequestTest2
 * @Description //TODO 
 * @Author zhaoyu
 * @Date 2023/10/31
 */
@WebServlet(urlPatterns = "/forwardRequestTest2.do")
//目标组件
public class forwardRequestTest2 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("forwardRequestTest2 invoked");
        System.out.println(req.getParameter("money"));

        //做出响应
        //处理中文乱码
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8"); //告诉浏览器这是一个html文件,以utl-8来解码

        PrintWriter writer = resp.getWriter();
        writer.println("forwardRequestTest2 do response to client"); //打印在client上了
    }
}

Servlet1把req和resp对象转发给Servlet2,最终做出响应的是Servlet2。Servlet1和Servlet2的req和resp对象在内存中是同一个
在forward转发模式下,请求应该完全交给目标组件去处理,我们在源组件中不要做出任何的响应处理。我们做出的任何响应处理也都是无效的。(比如设置响应头,响应体等…)
在forward方法调用之后,源组件也不要再使用req和resp对象做其他操作了,全部托管给目标组件
forward请求转发
Servlet1向resp中设置的任何数据,通过resp对象做出的任何响应,都不会响应给浏览器。都是无效的,响应这个动作完全托管给了Servlet2

7.include请求转发(了解)

forward转发,响应完全交给目标组件去做。
include处理流程
1.如果目标资源为Servlet或JSP,就调用他们的响应的service方法,把该方法产生的响应正文添加到源Servlet的响应结果中;如果目标组件为HTML文档,就直接把文档的内容添加到源Servlet的响应结果中。
2.返回到源Servlet的服务方法中,继续执行后续代码块。最后由Servlet1做出响应。

RequestDispatcher requestDispatcher = req.getRequestDispatcher("servlet2.do")
requestDispatcher.forward(req,resp);

include请求转发
include()转发处理的特点:
include与forward转发相比,包含以下特点:
1.源Servlet与被包含的目标资源的输出数据都会被添加到响应结果中。
2.在目标资源中对响应状态码或者响应头所做的修改都会被忽略。

总结
1.forward:完全托管(用得多)我们设置响应头的动作也交给目标资源,不要做任何动作,完全托管给目标资源做数据的接收->响应头设置->响应正文设置->响应。 forward请求转发
2.include:目标资源完成部分工作后返回给源资源

8.请求转发总结

  1. 请求转发是一种服务器行为,从浏览器看url仍然会显示源组件的路径,即请求转发行为对浏览器来说是不可知的。
  2. 请求的参数是可以传递的,从源组件->目标组件。因为HttpRequest对象和HttpResponse对象并没有重新创建,源组件和目标组件用的是同一个对象。
  3. 目标组件也可以是静态资源,可以通过这样的方式来实现页面跳转的效果,从浏览器看url仍是源组件的路径。(只是静态资源无法接收参数做出对应的处理)
RequestDispatcher requestDispatcher = req.getRequestDispatcher("aaa.html")
requestDispatcher.forward(req,resp)
  1. WEB-INF目录下的资源是受保护的,如请求WEB-INF/bbb.html是访问不到的(返回404)。但是如果通过请求转发的方式是可以访问到WEB-INF下的资源的。但是从浏览器看仍然是源组件的路径,浏览器对跳转动作无感知。
RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/bbb.html")
requestDispatcher.forward(req,resp)
  1. 请求转发是在当前项目下去找资源,所以请求转发只能转发给当前项目的内部资源,不能转发给当前项目的外部资源。
RequestDispatcher requestDispatcher = req.getRequestDispatcher("https://www.baidu.com")
requestDispatcher.forward(req,resp) //请求转发不能访问外部资源
  1. 常用forward,include作为了解。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值