JSP是JavaServer Page的缩写,也就是服务端网页。
一、概述
1.1 为什么使用JSP
在很多动态网页中,绝大部分内容都是固定不变的,只有局部内容需要动态产生和改变。JSP是简化Servlet编写的一种技术,它将Java代码和HTML语句混合在同一个文件中编写,只对网页中的要动态产生的内容采用Java代码来编写,而对固定不变的静态内容采用普通静态HTML页面的方式编写。
如果仅仅为了少量的动态内容而用Java代码来生成整个网页,会有什么不便的地方?
程序员真的很难:
如果使用Servlet程序来输出只有局部内容需要动态改变的网页,其中所有的静态内容也需要程序员用Java程序代码产生,整个Servlet程序的代码将非常臃肿,编写和维护都将非常困难。 对大量静态内容的美工设计和相关HTML语句的编写,并不是程序员所要做的工作,程序员对此也不一定在行。网页美工设计和制作人员不懂Java编程,更是无法来完成这样的工作。
使用Java生成HTML代码,这些HTML代码将会包裹在双引号当中,即字符串的形式。代码编辑器无法检查HTML代码,而且会有冗长的行结束符\r\n,HTML里的引号还需要进行转义。
示例:
PrintWriter writer =Response.getWriter();
writer.append("\r\n")
.append("\r\n")
.append("
\r\n").append(" \"Hello World!\"\r\n")
.append(" \r\n")
.append("\r\n");
于是,可以想到一种混合解决方案,在页面中而不是后台结合Java代码和HTML标签。绝大部分的静态页面采用固定的HTML标签,在HTML标签中嵌入运行Java代码的功能,JSP技术应运而生。
1.2 运行原理
JSP本质上是Servlet
Web容器(Servlet引擎)接收到以.jsp为扩展名的URL的访问请求时,它将把该访问请求交给JSP引擎去处理。Tomcat默认每个JSP 页面在第一次被访问时,JSP引擎将它翻译成一个Servlet源程序,接着再把这个Servlet源程序编译成Servlet的class类文件,然后再由Web容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。
JSP规范也没有明确要求JSP中的脚本程序代码必须采用Java语言,JSP中的脚本程序代码可以采用Java语言之外的其他脚本语言来编写。但是,JSP页面最终必须转换成Java Servlet程序。
如果担心第一次访问时编译JSP影响性能,可以在Web应用程序正式发布之前,将所有JSP页面预先编译成Servlet程序。
JSP工作流程:
客户端发送请求给web容器
web容器将jsp首先转译成servlet源代码
web容器将servlet源代码编译成.class 文件
web容器执行.class 文件
web容器将结果响应给客户端
JSP的编译
JSP会被翻译成.java放Tomcat/work/Catalina/localhost/***/org/apache/jsp/page,然后编译成.class。一个JSP页面的生成的.java文件如下:
public class My$jsp extendsHttpJspBase {static{}publicdate$jsp() {}private static boolean _jspx_inited = false;public final void _jspx_init() throwsorg.apache.jasper.runtime.JspException {};public void _JSP pageservice(HttpServletRequest request, HttpServletResponse response) throwsjava.io.IOException, ServletException {
JspFactory _jspxFactory= null;
PageContext pageContext= null;
HttpSession session= null;
ServletContext application= null;
ServletConfig config= null;
JspWriter out= null;
Object page= this;
String _value= null;try { if (_jspx_inited == false) {synchronized (this) {if (_jspx_inited == false) {
_jspx_init(); _jspx_inited= true;
}
}
}
_jspxFactory=JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext= _jspxFactory.getPageContext(this, request, response, "", true, 8192, true);
application=pageContext.getServletContext();
config=pageContext.getServletConfig();
session=pageContext.getSession();
out=pageContext.getOut();//HTML//begin
[file="/date.jsp";
from=(0,0);to=(7,6)] out.write("\r\n\r\n\r\n" + "\r\n\r\n\r\n" + "The date is\r\n");//end//begin
[file="/date.jsp";from=(7,8);to=(7,57)] out.println((newjava.util.Date()).toString());//end//HTML//begin
[file="/date.jsp";from=(7,59);to=(10,7)] out.write("\r\n \r\n \r\n");//end
} catch(Throwable t) {if (out != null && out.getBufferSize() != 0) {
out.clearBuffer();
}if (pageContext != null) {
pageContext.handlePageException(t);
}
}finally{if (_jspxFactory != null) {
_jspxFactory.releasePageContext(pageContext);
}
}
}
}
最重要的函数就是pageservice,web容器在编译好一个JSP类以后,就申请这个类的对象,并且直接调用pageservice来获得Response,最后返回给客户。
所有的JSP页面翻译出来的class,都从HttpJspBase继承,并且命名为PageName$jsp。在第一次调用pageservice函数的时候,该class会进行一次初始化,而这个初始化函数是_jspx_init,我们还可以自定义这个函数来实现JSP页面的初始化。
HTML代码直接被写到PrintWriter里面回馈给用户。
为什么JSP页面有那么多省写方式,比如说session , out , page , context之类。
这都是在pageservice里面定义的临时变量,每一次调用JSP页面,这些变量都会被重新初始化一次。当然我们也可以方便的声明自己的变量。
指令只是一个一个的对应为response.setContentType()的语句而已。
JSP页面转向这个语句被翻译为getServletContext().getRequestDispatcher("/List.jsp").forward(req, res);语句。
二、创建一个JSP
概览
模板元素:
JSP页面中的静态HTML内容称之为JSP模板元素,在静态的HTML内容中可以嵌套JSP的其他各种元素来产生动态内容和执行业务逻辑JSP模板元素定义了网页的基本骨架,即定义了页面的结构和外观。
所有JSP中已经隐含式地包含了一个标签库,那就是JSP标签库(前缀为jsp),使用它不用在JSP中添加taglib指令(不过需要为jsp标签添加XMLNS声明)。
几个JSP常用标签:
标签标签
后面会介绍Java标准标签库
标签语法:
content
prefix表示JSP标签库前缀,也被称为命名空间(XML术语),tagname是TLD中定义的标签名称。特性值将使用单引号或者双引号括起来,特性之间需要空白。
几种结构:
指令用于指示JSP解释器执行某个操作(例如设置内容类型)或者对文件作出假设(例如使用的是哪种脚本语言)、导入类、在转换时包含其他JSP或者包含JSP标签库。
声明用于在JSP Servlet类的范围内声明一些东西,例如可以定义实例变量、方法或声明标签中的类。要记住:这些声明都将出现在自动生成的JSP Servlet类中,所以声明中定义的类实际上是JSP Servlet类的内部类。声明中的代码将在转换时被复制到JSP Servlet类的主体中,并且它们可用于声明某些字段、类型或方法。
脚本中也包含Java代码,但不同于声明,脚本有不同的作用域,脚本被复制到_jspService方法(Tomcat 8.0)中,而非Servlet类的主体中(即和_jsService方法地位相同)。_jspService方法中的所有局部变量都可以在脚本中使用,任何在该方法体中合法的代码在脚本中也是合法的。所以,脚本中定义的是局部字段而非实例字段。还以在脚本中使用条件语句、操作对象和执行数学计算,这些在声明中都无法完成。我们甚至可以在脚本中定义类,这些类是_jspService方法中有效。声明中定义的类、方法或变量都可以在脚本中使用,但脚本中定义的类或变量不能在声明中使用。
表达式包含了一些简单的Java代码,可用于向客户端输出一些内容,它将把代码的返回值变量输出到客户端。任何赋值表达式的右侧都可以用在表达式中,表达式的作用域与脚本相同,如同脚本一样,表达式也将被复制到_jspServlet方法中。
映射JSP:
JSP就是一个Servlet,因此我们在web.xml文件中也可以使用标签和标签来映射JSP,使JSP能有一个虚拟目录(对外访问路径)。
myjsp
/MyJsp.jsp
myjsp
/myhtml.html
注释
在JSP中可以使用4种注释:
XML注释:
传统的Java注释: // 这是注释
传统的Java块注释: /* 这是注释 */
JSP注释:
2.1 声明
JSP声明将Java代码封装在之中,它里面的代码将被插入进Servlet的_jspService方法的外面,所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法。
多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。
JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。
voidtest(){}%>
几乎不用
2.2 脚本——在JSP中使用Java代码(不推荐)
JSP脚本片断(scriptlet)是指嵌套在之中的一条或多条Java程序代码。
在JSP脚本片断中,可以定义变量、执行基本的程序运算、调用其他Java类、访问数据库、访问文件系统等普通Java程序所能实现的功能。
JSP脚本片断中的Java代码将被原封不动地搬移进由JSP页面所翻译成的Servlet的_jspService方法中,所以,JSP脚本片断之中只能是符合Java语法要求的程序代码,除此之外的任何文本、HTML标记、其他JSP元素都必须在脚本片断之外编写。
JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每条命令执行语句后面必须用分号(;)结束。
在一个JSP页面中可以有多个脚本片断(每个脚本片断代码嵌套在各自独立的一对之间),在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。
int x = 3;%>
这是一个HTML段落
for
>www.it315.org>
if...else...
其他元素其他元素
隐含变量
隐含变量是不用定义或声明就能使用的变量,可以在脚本和表达式中使用request、response、pageContext、session、application、config、out、page这8个隐含对象,实际上还可以使用一个叫exception的隐含对象。
JSP规范要求JSP的转换器和编译器提供这些变量,并且名字也要完全相同。这些隐含变量实际定义在JSP执行的Servlet方法(Tomcat 8.0中是_jspServlet方法)的开头。因为隐式的变量定义在_jspServlet中,所以定义在类中的声明不能使用它们。表达式也在_jspServlet中,可以使用。
下面我们新建了一个空白的JSP文件,访问该文件,在Tomcat中生成了java文件和编译的.class文件
在tomcat_home\work\Catalina\localhost\project_name\org\apache找到java文件:
public void _jspService(final javax.servlet.http.HttpServletRequest request, finaljavax.servlet.http.HttpServletResponse response)throwsjava.io.IOException, javax.servlet.ServletException {final java.lang.String _jspx_method =request.getMethod();if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,"JSPs only permit GET POST or HEAD");return;
}finaljavax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;finaljavax.servlet.ServletContext application;finaljavax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out= null;final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out= null;
javax.servlet.jsp.PageContext _jspx_page_context= null;try{
response.setContentType("text/html; charset=UTF-8");
pageContext= _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
_jspx_page_context=pageContext;
application=pageContext.getServletContext();
config=pageContext.getServletConfig();
session=pageContext.getSession();
out=pageContext.getOut();
_jspx_out=out;
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("
\r\n");out.write("\r\n");
out.write("
Insert title here\r\n");out.write("\r\n");
out.write("
\r\n");out.write("\r\n");
out.write(" ");
Person person= newPerson();
System.out.println(person.getPersonInfo());
out.write("\r\n");
out.write(" \r\n");
out.write("\r\n");
out.write("");
}catch(java.lang.Throwable t) {if (!(t instanceofjavax.servlet.jsp.SkipPageException)){
out=_jspx_out;if (out != null && out.getBufferSize() != 0)try{if(response.isCommitted()) {
out.flush();
}else{
out.clearBuffer();
}
}catch(java.io.IOException e) {}if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);else throw newServletException(t);
}
}finally{
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
request:HttpServletRequest的一个对象
response:HttpServletRespons的一个对象,在JSP页面中几乎不用
pageContext:页面的上下文,是PageContext的一个对象,可以从该对象中获取到其他8个隐含对象,也可以从中获取到当前页面的其他信息,后面自定义标签时使用
HttpServletRequest req =pageContext.getRequest();
System.out.println(req== request);
结果为true
session:代表浏览器和服务器的一次会话,是HttpSession的一个对象。
获得会话的ID
System.out.println(session.getId());
application:代表当前Web应用,是ServletContext对象
config:是当前JSP对应的Servlet的ServletConfig对象,几乎不用
out:JspWriter对象,调用out.println()可以直接把字符串打印到浏览器上
out.println("hello");
out.println("
"); 换行
out.println("world");
page:指向当前JSP对应的Servlet对象的引用,但为Objetc类型,只能调用Object类的方法,几乎不用
exception有下面的声明才能用
pageContext、request、 session、 application 对属性的作用域的范围从小到大
在application、session、request、pageContext对象中都可以调用setAttribute方法和getAttribute方法来设置和检索各自域范围内的属性。这四个对象也称为域对象。
存储在application对象中的属性可以被同一个Web应用程序中的所有Servlet和JSP页面访问。
存储在session对象中的属性可以被属于同一个会话的所有Servlet和JSP页面访问,浏览器打开直到关闭是一次会话(会话未失效的前提下)。
存储在request对象中的属性可以被属于同一个请求的所有Servlet和JSP页面访问,例如使用PageContext.forward和PageContext.include方法连接起来的多个Servlet和JSP页面。
存储在pageContext对象中的属性仅可以被当前JSP页面的当前响应过程中调用的各个组件访问,例如,正在响应当前请求的JSP页面和它调用的各个自定义标签类。
PageContext类中还提供了对各个域范围的属性进行统一管理的方法,以简化对各个域范围内的属性的访问。
setAttribute方法 设置属性
public voidsetAttribute(java.lang.String name,java.lang.Object value)public void setAttribute(java.lang.String name,java.lang.Object value,int scope)
常量
PageContext.APPLICATION_SCOPE
PageContext.SESSION_SCOPE
PageContext.REQUEST_SCOPE
PageContext.PAGE_SCOPE
getAttribute方法 获取指定的属性
publicjava.lang.Object getAttribute(java.lang.String name)public java.lang.Object getAttribute(java.lang.String name,int scope)
removeAttribute方法
public voidremoveAttribute(java.lang.String name)public void removeAttribute(java.lang.String name,int scope)
getAttributeNamesInScope方法
findAttribute方法 (*)
设置
request.setAttribute("requesAttr", "requesAttr");
session.setAttribute("sessionAttr", "sessionAttr");
application.setAttribute("applicationAttr", "applicationAttr");%>
获取
pageContextAttr:requestAttr:sessionAttr:applicationAttr:
2.3 使用指令
JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。
JSP指令的基本语法格式:
举例:
注意:属性名部分是大小写敏感的
在JSP 2.0中,定义了page、include和taglib这三种指令,每种指令中又都定义了一些各自的属性。
如果要在一个JSP页面中设置同一条指令的多个属性,可以使用多条指令语句单独设置每个属性,也可以使用同一条指令语句设置该指令的多个属性。
第一种方式:
第二种方式:
2.3.1 page指令
page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。
JSP 2.0规范中定义的page指令的完整语法:
[ language="java"]
[extends="package.class"]
[import="{package.class | package.*}, ..."]
[ session="true | false"]
[ buffer="none | 8kb | sizekb"]
[ autoFlush="true | false"]
[ isThreadSafe="true | false"]
[ info="text"]
[ errorPage="relative_url"]
[ isErrorPage="true | false"]
[ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ] 指定当前JSP页面的响应类型(前面word文档方式的例子),实际调用的是respons.setContentType()方法,对于JSP而言一般取text/html;charset=UTF-8 ,即html类型文件,字符编码为utf-8[ pageEncoding="characterSet | ISO-8859-1"] 指定当前JSP页面的字符编码,通常与contentType中charset一致。
[ isELIgnored="true | false"] 指当前JSP页面是否可以使用EL表达式,通常取值为true%>
导入类
无论何时在JSP包中包含直接使用类的Java代码,该JSP要么使用完全限定类名,要么在JSP文件中添加一条导入命令。
对于不产生输出的JSP标记、指令、声明和脚本,它们将会在客户端输出一行空白。所以,如果在变量声明和脚本之前有许多导入类的page指令,那么将会在输出中显示出数行空白。为了解决这个问题,通常会将一个标记的尾部与另一个标记的头部连接在一起:
%>
%>
错误页
当前页面如果出错,会自动把error.jsp当作出错页面
在erro.jsp这个错误页面里可以添加如下,以显示错误信息,用到了转发,因为错误信息实际上是在跳转到error.jsp之前的那个页面的
Error Message:
但是,要想使用上面的exception这个隐含对象,必须在网页里添加
errorPage属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前Web应用程序的根目录(注意不是站点根目录),否则,表示相对于当前页面。
可以在web.xml文件中使用元素为整个WEB应用程序设置错误处理页面,其中的子元素指定异常类的完全限定名,元素指定以“/”开头的错误处理页面的路径。
404
/WEB-INF/erro.jsp
java.lang.ArithmeticException
/WEB-INF/erro.jsp
如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。
在响应error.jsp时,JSP引擎使用的是请求转发的方式,
如何时客户不直接访问某一个页面?一般情况下,对于Tomcat服务器而言,WEB-INF下的文件是不能通过在浏览器中直接输入来访问的,但通过请求的转发是可以的。
contentType
JSP引擎会根据page指令的contentType属性生成相应的调用ServletResponse.setContentType方法的语句。
page指令的contentType属性还具有说明JSP源文件的字符编码的作用。
2.3.2 包含其他JSP
include指令用于通知JSP引擎在翻译当前JSP页面时将其他文件中的内容合并进当前JSP页面转换成的Servlet源文件中,这种在源文件级别进行引入的方式称之为静态引入,当前JSP页面与静态引入的页面紧密结合为一个Servlet。
语法:
其中的file属性用于指定被引入文件的相对路径。
b.jsp
BBB PAGE
a.jsp
AAA PAGE
访问a.jsp,结果如下:
被引入的文件必须遵循JSP语法,其中的内容可以包含静态HTML、JSP脚本元素、JSP指令和JSP行为元素等普通JSP页面所具有的一切内容。
被引入的文件可以使用任意的扩展名,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用.jspf(JSP fragments)作为静态引入文件的扩展名。
在将JSP文件翻译成Servlet源文件时,JSP引擎将合并被引入的文件与当前JSP页面中的指令元素(设置pageEncoding属性的page指令除外)。所以,除了import和pageEncoding属性之外,page指令的其他属性不能在这两个页面中有不同的设置值。
除了指令元素之外,被引入的文件中的其他元素都被转换成相应的Java源代码,然后插入进当前JSP页面所翻译成的Servlet源文件中,插入位置与include指令在当前JSP页面中的位置保持一致。
引入文件与被引入文件是在被JSP引擎翻译成Servlet的过程中进行合并,而不是先合并源文件后再对合并的结果进行翻译。当前JSP页面的源文件与被引入文件的源文件可以采用不同的字符集编码,即使在一个页面中使用page指令的pageEncoding或contentType属性指定了其源文件的字符集编码,在另外一个页面中还需要用page指令的pageEncoding或contentType属性指定其源文件所使用的字符集 。
Tomcat 5.x在访问JSP页面时,可以检测它所引入的其他文件是否发生了修改,如果发生了修改,则重新编译当前JSP页面
file属性的设置值如果使用相对路径,表示相对于当前文件所在目录开始定位包含文件。如果以“/”开头,使用绝对路径,表示从Web应用程序的根目录开始定位该文件。
举例:
下面都是相对路径,因此都是a.jsp同级目录下找b.jsp
假设myweb应用程序的根目录下有一个a.jsp文件,其一般的访问路径形式为:http://localhost:8080/myweb/a.jsp
在a.jsp页面中使用了如下语句引入b.jsp文件:
这时候JSP引擎调用的b.jsp文件的完整URL路径为什么?
http://localhost:8080/myweb/b.jsp
如果将a.jsp页面映射为如下地址:http://localhost:8080/myweb/dir1/a.html
这时候JSP引擎调用的b.jspf文件的完整URL路径为:
http://localhost:8080/myweb/dir1/b.jsp
2.3.3 包含标签库
如果希望JSP中使用标签库中定义的标签,使用taglib指令引用该标签库即可。
2.4 使用标签
JSP还提供了一种称之为Action的元素,在JSP页面中使用Action元素可以完成各种通用的JSP页面功能,也可以实现一些处理复杂业务逻辑的专用功能。 Action元素采用XML元素的语法格式,即每个Action元素在JSP页面中都以XML标签的形式出现。JSP规范中定义了一些标准的Action元素,这些元素的标签名都以jsp作为前缀,并且全部采用小写,例如,、等等。
标签
标签
标签与include指令
标签用于把另外一个资源的输出内容插入进当前JSP页面的输出内容之中,这种在JSP页面执行时的引入方式称之为动态引入。
语法:
page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。
动态引入:并不像include指令生成一个Servlet源文件,而是生成两个Servlet源文件,然后通过一个方法把目标页面包含进来。
标签是在当前JSP页面的执行期间插入被引入资源的输出内容,当前JSP页面与被动态引入的资源是两个彼此独立的执行实体,被动态引入的资源必须是一个能独立被WEB容器调用和执行的资源。include指令只能引入遵循JSP格式的文件,被引入文件与当前JSP文件共同合被翻译成一个Servlet的源文件。
使用标签和include指令都可以把一个页面的内容分成多个组件来生成,开发者不必再把页眉和页脚部分的相同HTML代码复制到每个JSP文件中,从而可以更轻松地完成维护工作,但是都应注意最终的输出结果内容应遵循HTML语法结构,例如,如果当前页面产生了、、、等标记,那么在被引入文件中就不能再输出、、、等标记。
标签对JSP引擎翻译JSP页面的过程不起作用,它是在JSP页面的执行期间才被调用,因此不会影响两个页面的编译。由于include指令是在JSP引擎翻译JSP页面的过程中被解释处理的,所以它对JSP引擎翻译JSP页面的过程起作用,如果多个JSP页面中都要用到一些相同的声明,那么就可以把这些声明语句放在一个单独的文件中编写,然后在每个JSP页面中使用include指令将那个文件包含进来。
标签使用page属性指定被引入资源的相对路径,而include指令使用file属性指定被引入资源的相对路径。
标签
用于把请求转发给另外一个资源。
语法:
page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。
相当于
RequestDispatcher.forward方法、PageContext.forward方法、标签的区别
调用RequestDispatcher.forward方法的JSP脚本代码的前后不能有JSP模版内容。
调用PageContext.forward方法的JSP脚本代码的后面不能有JSP模版内容。
标签的前后都能有JSP模版内容。
当使用和标签引入或将请求转发给的资源是一个能动态执行的程序时,例如Servlet和JSP页面,那么,还可以使用
语法1:
语法2:
a.jsp
b.jsp
b.jsp可以获取a.jsp的username参数
三、结合使用Servlet和JSP
3.1 转发和重定向
本质区别:请求的转发只发出了一次请求,而重定向则发出了两次请求。
请求的转发地址栏是初次发出请求的地址,重定向地址栏不再是初次发出的请求地址,地址栏为最后响应的那个地址。
请求转发:在最终的Servlet中,request对象和中转的那个request是同一个对象,而请求的重定向不是同一个对象。
Servlet实例
转发Servlet
public class ForwardServlet extendsHttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException{
System.out.println("ForwardServlet's doGet.");//请求的转发://1.调用HttpServletRequest的getRequestDispatcher()方法获取RequestDispatcher对象//调用getRequestDispatcher()需要转入要转发的地址//2.调用HttpServletRequest的forward(request, response)进行请求的转发
String path = "testServlet";
RequestDispatcher requestDispatcher= request.getRequestDispatcher("/"+path);
requestDispatcher.forward(request, response);
}
}
重定向Servlet
public class RedirectServlet extendsHttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException{
System.out.println("RedirectServlet's doGet");//执行重定向,直接调用response.sendRedirect(path)方法//path为重定向的地址
String path = "testServlet";
response.sendRedirect(path);
}
}
定义一个用于测试的Servlet
public class TestServlet extendsHttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
System.out.println("TestServlet's doGet 方法. ");}
}
配置web.xml
forwardServlet
com.atguigu.javaweb.ForwardServlet
forwardServlet
/forwardServlet
redirectServlet
com.atguigu.javaweb.RedirectServlet
redirectServlet
/redirectServlet
testServlet
com.atguigu.javaweb.TestServlet
testServlet
/testServlet
HTML
test.html
Titletest.html浏览器页面如下
点击Forward,地址栏是该按钮指向的地址
控制台打印:
ForwardServlet's doGet.
TestServlet's doGet方法
说明执行了TestServlet
点击Redirect,地址栏不是该按钮指向的地址
控制台打印:
ForwardServlet's doGet.
TestServlet's doGet方法
说明执行了TestServlet
都执行了TestServlet
但是定向没有跳转到TestServlet对应的网址
而重定向跳转到了TestServlet对应的网址
RequestDispatcher实例对象
RequestDispatcher实例对象是由Servlet引擎创建的,它用于包装一个要被其他资源调用的资源(例如,Servlet、HTML文件、JSP文件等),并可以通过其中的方法将客户端的请求转发给所包装的资源。
RequestDispatcher接口中定义了两个方法:forward方法和include方法。
forward和include方法接收的两个参数必须是传递给当前Servlet的service方法的那两个ServletRequest和ServletResponse对象,或者是对它们进行了包装的ServletRequestWrapper 或ServletResponseWrapper对象。
获取RequestDispatcher对象的方法:
ServletContext.getRequestDispatcher (参数只能是以“/”开头的路径)
ServletContext.getNamedDispatcher
ServletRequest.getRequestDispatcher (参数可以是不以“/”开头的路径)
用sendRedirect方法实现请求重定向
sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,它还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。如果传递给sendRedirect 方法的相对URL以“/”开头,则是相对于整个WEB站点的根目录,而不是相对于当前WEB应用程序的根目录。
请求重定向与请求转发的比较
RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。
调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。
HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。
RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
3.2 JSP属性和JSP属性组
3.2 将Servlet请求转发给JSP
由Servlet接收请求,实现业务逻辑处理以及必需的数据存储或读取,创建可以由JSP轻松处理的数据模型,最终将请求转发给JSP。