接下来介绍Servlet中另外重要知识点——请求转发、包含。
请求转发:Servlet(源组件)先对客户请求做一些预处理操作(数据处理),然后把请求转发给其他Web组件(目标组件,可以是Servlet、Jsp、Html等)来完成包括生成响应结果在内的后续操作。
请求转发:Servlet(源组件)先对客户请求做一些预处理操作(数据处理),然后把请求转发给其他Web组件(目标组件,可以是Servlet、Jsp、Html等)来完成包括生成响应结果在内的后续操作。
示意图:
两者的共同点:
(1)、源组件和目标组件处理的都是同一客户请求,源组件和目标组件共享同一个ServletRequest和ServletResponse对象;
(2)、目标组件可以为Servlet、JSP或者html文档;
(3)、都依赖于javax.servlet.RequestDispatcher接口
这边提到一个接口javax.servlet.RequestDispatcher,先来看它的定义:
Javax.servlet. RequestDispatcher接口表示请求分发器,它有两个方法:
(1)、forword():把请求转发给目标组件:
Public void forward(ServletRequest req,ServletResponse res)throws ServletException,java.io.IOException
(2)、include():包含目标组件的响应结果:
Public void forward(ServletRequest req,ServletResponse res)throws ServletException,java.io.IOException
结合实例来解释,定义一个ForwardServlet,具体代码如下:
package com.java.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardServlet extends HttpServlet{
private static final long serialVersionUID = 674344393733876756L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
//获取参数值
String username = req.getParameter("username");
//将username存储到request(请求)范围内
req.setAttribute("username", username);
//跳转到index.jsp页面
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}
之后再WebRoot目录下的index.jsp标签加入一句脚本代码
pageEncoding改为UTF-8编码,不然输出中文会乱码
配置信息:
<servlet>
<servlet-name>ForwardServlet</servlet-name> <servlet-class>com.java.web.servlet.ForwardServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ForwardServlet</servlet-name>
<url-pattern>/ForwardServlet</url-pattern>
</servlet-mapping>
重启Tomcat,在浏览器地址栏输入:
http://localhost:8080/Servlet16_10_3/ForwardServlet?username=张三
测试结果:
做一个简单的分析:
与之前的例子稍微不同的是,这次添加了一个Jsp组件,用来显示Servlet存储的从客户获取的参数值。按理说,index.jsp和ForwardServlet应该是不相关的组件,相互独立的,但是index.jsp却能获取到Servlet中HttpServletRequest对象存储的信息,这说明在同一个请求内,ForwarServlet和index.jsp共享同一个请求对象。
这里面有HttpServletRequest对象两个常用的方法
setAttribute(String str, Object obj)设置属性值
getAttribute(String str)获取属性值
这两者一般是成对存在,都是运行在服务端内部,与客户端无关。
getParameter(String str)是用来获取客户端传递的参数值。
接下来做个包含(include)的测试
定义一个IncludeServlet,具体代码如下:
package com.java.web.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class IncludeServlet extends HttpServlet{
private static final long serialVersionUID = 953520154284566773L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.write("<html><body>");
RequestDispatcher headDispatcher = req.getRequestDispatcher("head.html");
RequestDispatcher bodyDispatcher = req.getRequestDispatcher("BodyServlet");
RequestDispatcher footDispatcher = req.getRequestDispatcher("foot.html");
headDispatcher.include(req, resp);
bodyDispatcher.include(req, resp);
footDispatcher.include(req, resp);
writer.write("</body></html>");
writer.flush();
writer.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}
先定义一个实体类MyUser
然后定义一个被包含的BodyServlet
package com.java.web.servlet;
import java.io.IOException;
import java.io.PrintWriter;
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 com.test.entity.MyUser;
/**
* Servlet implementation class BodyServlet
*/
public class BodyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");;
PrintWriter writer = response.getWriter();
MyUser mUser = new MyUser();
mUser.setUserName("张三");
writer.write(mUser.getUserName());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
然后在WebRoot路径先新建head.html和foot.html
head.html :
foot.html :
配置信息:
<servlet>
<servlet-name>IncludeServlet</servlet-name>
<servlet-class>com.java.web.servlet.IncludeServlet
</servlet-class>
</servlet>
<servlet>
<servlet-name>BodyServlet</servlet-name>
<servlet-class>com.java.web.servlet.BodyServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>IncludeServlet</servlet-name>
<url-pattern>/IncludeServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>BodyServlet</servlet-name>
<url-pattern>/BodyServlet</url-pattern>
</servlet-mapping>
测试结果:
include最大特点:是会将web应用组件的运行结果当作被包含的对象。比如以上的BodyServlet,BodyServlet会先在服务端运行,将运行后的结果返回给IncludeServlet。
接下来学习一个与请求转发有相同功能而实现原理却截然不同的跳转——重定向(redirect)
简单说下重定向:
当浏览器想服务器发送请求,请求一个资源时(可以是一个页面),servlet接收到这个请求,发现这个资源是存放在其他地方的,于是Servlet通过HttpServletResponse对象的返回一个该请求资源的正确URL给浏览器,浏览器根据服务器返回的请求资源的URL,重新发送请求。因此在整个重定向过程中,总共发送了两次请求。
可以用之前的ForWardServlet例子,稍微变动下,做个测试。
依然在浏览器地址栏输入:
http://localhost:8080/Servlet16_10_3/ForwardServlet?username=张三
测试结果:
从结果上看,index.jsp并没有获取到username的值,说明ForwardServlet并没有和index.jsp共享同一个请求,而实际上重定向是有两个请求,数据不共享。
Forward和Redirect的不同也可以从地址栏的请求地址区别出来。
Forward地址栏的URL是
http://localhost:8080/Servlet16_10_3/ForwardServlet?username=张三 属于第一个请求
Redirect的地址栏URL是
http://localhost:8080/Servlet16_10_3/index.jsp
属于第二个请求
从刚才的例子知道重定向是含有两个请求,两个请求间的数据是不能共享,因此index.jsp无法获取到第一个请求中的参数值,那现在如果想用重定向方法跳转到index.jsp页面,同时又可以获取到客户的请求参数,该如何做呢?