文章目录
3.Servlet
1.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对象为所有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-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叫目标组件。
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对象做其他操作了,全部托管给目标组件
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与forward转发相比,包含以下特点:
1.源Servlet与被包含的目标资源的输出数据都会被添加到响应结果中。
2.在目标资源中对响应状态码或者响应头所做的修改都会被忽略。
总结
1.forward:完全托管(用得多)我们设置响应头的动作也交给目标资源,不要做任何动作,完全托管给目标资源做数据的接收->响应头设置->响应正文设置->响应。 forward请求转发
2.include:目标资源完成部分工作后返回给源资源
8.请求转发总结
- 请求转发是一种服务器行为,从浏览器看url仍然会显示源组件的路径,即请求转发行为对浏览器来说是不可知的。
- 请求的参数是可以传递的,从源组件->目标组件。因为HttpRequest对象和HttpResponse对象并没有重新创建,源组件和目标组件用的是同一个对象。
- 目标组件也可以是静态资源,可以通过这样的方式来实现页面跳转的效果,从浏览器看url仍是源组件的路径。(只是静态资源无法接收参数做出对应的处理)
RequestDispatcher requestDispatcher = req.getRequestDispatcher("aaa.html")
requestDispatcher.forward(req,resp)
- WEB-INF目录下的资源是受保护的,如请求WEB-INF/bbb.html是访问不到的(返回404)。但是如果通过请求转发的方式是可以访问到WEB-INF下的资源的。但是从浏览器看仍然是源组件的路径,浏览器对跳转动作无感知。
RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/bbb.html")
requestDispatcher.forward(req,resp)
- 请求转发是在当前项目下去找资源,所以请求转发只能转发给当前项目的内部资源,不能转发给当前项目的外部资源。
RequestDispatcher requestDispatcher = req.getRequestDispatcher("https://www.baidu.com")
requestDispatcher.forward(req,resp) //请求转发不能访问外部资源
- 常用forward,include作为了解。