JSP内置对象、指令和动作
一、内置对象
在 JSP 的 Java 代码块、表达式块等中可以直接使用的引用,称为 JSP 的内置对象。常用的内置对象有九个,分别是:
这九个对象在 JSP 的 Java 代码块、表达式块中可以直接使用。之所以可以直接使用,是因为 Java 代码块与表达式块被 JSP 引擎翻译后均出现在了 Servlet 的_jspService()方法中。而这九个对象,就_jspService()方法中的局部变量。在 JSP 的 Java 代码块、表达式块中的代码就是_jspService()方法中的代码,所以在其中可以直接使用。
1.1 pageContext
pageContext,页面上下文,其具有一个只在当前页面范围的域属性空间,即其具有setAttribute()方法与 getAttribute()方法。但,由于在当前页面范围,数据不存放到域属性空间也可直接使用,将数据存放到域属性空间反而感觉“多此一举”,所以这些方法并不常用。不过,在同一页面中,为了使用 EL 表达式(后面学习)来访问某变量的值,此时一般使用
pageContext。pageContext 具有一些 get 方法,可以获取到 Request、Response、Session、ServletContext、ServletConfig、page(即当前 Servlet)、exception、out 等另外八个内置对象。
但由于这些对象本身就是内置对象,在 JSP 页面中可以直接使用,所以这些方法也并不常用。不过 EL 表达式中,将会经常使用 pageContext 的这些方法。
1.2 application
application,即 ServletContext。所以 ServletContext 所具有的方法,application 都具有。
常用的方法例如,
String getInitParameter ():
获取 web.xml文件的中指定名称的上下文参数值。例 如getInitParameter(“myDBDriver”);会返回字符串“com.mysql.jdbc.Driver”。Enumeration getInitParameterNames():获取 web.xml 文件的中的所有的上下文参数名称。其返回值为枚举类
型 Enumeration。void setAttribute(String name, Object object):
在 ServletContext 的公共数据空间中,也称为域属性空间,放入数据。这些数据对于 Web应用来说,是全局性的,与整个应用的生命周期相同。当然,放入其中的数据是有名称的,通过名称来访问该数据。Object getAttribute(String name):
从 ServletContext 的域属性空间中获取指定名称的数据。void removeAttribute(String name):
从 ServletContext 的域属性空间中删除指定名称的数据。String getRealPath(String path):
获取当前 Web 应用中指定文件或目录在本地文件系统中的路径,是基于盘符的路径。String getContextPath():
获取当前应用在 Web 容器中的名称。
1.3 out
out,类型为 javax.servlet.jsp.JspWriter。查看 JavaEE 文档,发现 JspWriter 类继承自 IO流的 Writer 类。即 out 就是一个输出流对象。
1.4 page
查看 JSP 翻译为的 Servlet,可以看到 page 对象即 Servlet 对象本身。这个对象在实际应用中并不常用。
1.5 exception
在普通的 JSP 页面中是不能使用 exception 内置对象的。因为打开 JSP 翻译为的 Servlet,发现其中并没有 exception 对象。若要在页面中直接使用 exception 对象,则需要配合着 page指令使用。
1.6 其它对象
其它对象,还有 request、response、session,及 config。它们的用法与Servlet的用法相同。只不过是直接使用在了 JSP 页面中了。
二、JSP指令
SP 指令的作用是为当前页面做一些基本的属性设置,为当前的页面的运行提供基本的环境。
根据功能的不同,JSP 中包含三类指令:page 指令,即页面指令;include 指令,即包含指令;及 taglib 指令,即标签库指令。无论哪种指令,其使用的语法格式均为如下形式:
<%@ 指令名称 属性名=属性值 属性名=属性值 ... %>
2.1 page指令
page 指令用于设置当前 JSP 页面的相关信息。一个 JSP 文件中可以包含多个 page 指令。常用的 page 指令的属性意义及用法如下:
(1)pageEncoding属性
pageEncoding 属性用于设置当前 JSP 页面所使用的字符编码格式。即,用户在浏览器中通过右击查看编码所看到的编码格式。
<%@ page pageEncoding="utf-8" %>
其被 JSP 翻译引擎翻译到 Servlet 中的语句,是_jspService()方法中的 setContentType()。
(2)contentType属性
contentType 属性用于设置当前 JSP 页面呈现于用户浏览器中的内容类型,通常为”text/html”类型,即 html 格式的文本。若在 JSP 页面中设置如下:
<%@ page contentType="text/html" %>
注:当内容类型为”text/html”时,使用 pageEncoding 属性与contentType 属性效果是相同的。只有当内容类型不为”text/html”时,才专门使用 contentType 属性指定。在指定字符编码时,这两个属性一般不同时使用。
(3)import属性
import 属性,用于完成在 JSP 页面中导入指定的类。其被 JSP 引擎翻译为 Servlet 中的import 语句。例如,
<%@ page import="java.util.Date" %>
若要导入多个类,则在 import 属性值中可使用逗号将这些类分隔。
<%@ page import="java.util.Date,java.sql.*"%>
(4)errorPage属性
errorPage 属性用于指定,当前页面运行过程中发生异常时所要跳转到的页面。
<%@ page errorPage="/error.jsp"%>
<%
int i = 3/0;
%>
注意,该属性会被翻译到 Servlet 的_jspService()方法中,即这里出现的路径是一个后台路径,而非前台路径。所以这里指定的发生异常后所要跳转的页面,需要使用以斜杠开头的后台路径。
(5)isErrorPage属性
若一个页面中指定了发生异常后所要跳转的页面,将会出现一个问题:异常信息被隐藏了。在控制台看不到异常信息,在所跳转的页面中也看不到异常信息。这对于程序员来说,不是件好事,没有足够的信息提示。
此时,一般是希望当异常发生后,在所要跳转的页面中能够给出相应的异常信息。而异常信息是封装在异常对象 exception 中的。也就是说,我们需要在所要跳转的页面中能够获取到异常对象。此时,就需要将这个所要跳转的页面指定为“错误处理页面”。
当一个页面的 page 指令中设置 isErrorPage 的值为 true 时,表明当前页面为一个“错误处理页面”。默认 isErrorPage 的值为 false。
<%@ page isErrorPage="true" pageEncoding="utf-8"%>
error page
<%
//在控制台输出异常的堆内存跟踪信息
exception.printStackTrace();
%>
一旦一个页面 page 指令的 isErrorPage 属性被指定为了 true,查看 JSP 页面所翻译为的Servlet 则会发现,在_jspService()方法中,多出了一个变量 exception。这就是内置对象exception,可以在 JSP 的 Java 代码块、表达式块中直接使用的内置对象。
(6)session属性
session 属性用于指定当前页面中是否可以直接使用内置对象 session。默认为 true,可以使用。查看JSP翻译为的Servlet,可以看到session的创建,使用的是无参方法getSession()。该方法的功能是,若当前具有 session,则使用当前的 session;若当前没有 session,则会新建一个 session。即 session 对象肯定不为 null。
但,有些情况下,我们希望的仅仅是获取到之前已经存在的 session,若之前没有 session,则并不需要创建 session,即让 session 为 null。显示使用默认的 session 属性为 true 已经无法满足需求了。此时,就需要将 session 属性设置为 false,即不使用内置对象session,而是在JSP 页面的代码块中使用 request 的带参getSession()方法。
HttpSession session = request.getSession(false);
若设置 session 属性的值为 false,查看生成的 Servlet 代码,会发现根本就没有出现内置对象 session。
2.2 include指令
include 指令,即包含指令,用于将指定的文件包含到当前的 JSP 文件中。该指令只有一个属性 file,用于指定要包含的文件。
(1)用法:被 include 指定包含的文件,可以是 JSP 动态页面文件,也可以是 HTML 静态页面文件。这里定义一个名为 left.jsp 的动态文件。其中定义了一个变量 sum。
left jsp page
<%
int sum=100;
%>
再定义一个 index.jsp,不仅将 left.jsp 文件包含了进来,还访问了变量 sum。
index jsp page
<br>
<%@ include file="/left.jsp" %>
<br>
<%=sum %>
运行结果:
index jsp page
left jsp page
100
(2)静态联编
查看 Tomcat 的 work 目录中相关子目录,发现只生成了一个 index_jsp.java 的 Servlet 源文件,并没有生成 left_jsp.java 文件。那是因为 JSP 翻译引擎在翻译时,会将 include 指令所指定的文件内容直接翻译到当前 JSP 对应的 Servlet 中,形成一个.java 文件。这就说明一个问题:这个包含操作是在编译之前完成的,是在编译之前由 JSP 翻译引擎完成的,不是在程序运行期完成的。这种包含是一种静态包含,称为静态联编。
由于在编译期就将这些文件合并为了一个 Servlet 文件,所以,整个过程就一个_jspService()方法。也就是说,这些文件之间是可以相互访问局部变量的。只要满足变量声明与使用的先后顺序即可。
(3)为什么使用include指令
对于一个包含很多页面的系统或站点,很多页面的头部、底部,或者左侧部分都是相同的。为了减少页面定义的重复性工作,为了便于对这些相同部分的修改,我们将这些相同的部分,分别定义为了多个页面。然后,让其它需要使用这些部分的页面,使用 include 指令将这些部分包含进来。这样的话,不仅大大减少了工作量,还做到了对于页面修改的“一改全改”效果。
三、JSP动作(Action)
在 JSP 页面中大量使用 Java 代码块、表达式块等内容,会使 JSP 页面看起来“杂乱无章”。为了使 JSP 页面看得简洁明了,为了简化 Java 代码,一般情况下,我们会尽量少的使用 Java代码块与表达式块。取而代之的则是使用 EL 表达式、JSTL 标签,及 JSP 动作。
JSP 动作是指,使用系统定义好的标签来完成本应由 Java 代码来完成的功能。
JSP 动作的语法格式为:
<jsp:动作名称 属性名=属性值 属性名=属性值 ....></jsp:动作名称>
或
<jsp:动作名称 属性名=属性值 属性名=属性值 .... />
JSP 动作很多,但在实际开发时常用的就两个:转发动作与包含动作。
这两份个动作的完成,底层使用的是 RequestDispatcher 的 forward()与 include()方法实现的。而这两份种请求转发方式的本质区别是,标准输出流的开启时间不同。forward()方式的标准输出流是在目标资源中开启的标准输出流,而 include()方式的标准输出流则是在当前发出包含运作的页面中开启的。所以,forward()动作的发起页面中是无法向标准输出流中写入数据的;而 include()动作的发起页面及目标页面中均可向标准输出流中写入数据。这两份个动作都具有一个 page 属性,用于指定要转向的页面。
3.1 forward动作
页面中一旦具有了forward动作,那么当前页面中的所有显示的内容都将无法显示。因为页面直接转发到了下一个页面。
(1)定义index.jsp页面
<%@ page contentType="text/html; charset=utf-8" %>
页面中只要具有了forward动作,当前页面中的所有要显示的内容都将无法显示。因为页面直接转发到了下一个页面。
<%
request.setAttribute("user","beijing");
%>
<jsp:forward page="/next.jsp"/>
(2)定义next.jsp页面
next page
<br>
<%=request.getAttribute("user") %>
打开 JSP 页面翻译为的 Servlet,可以看到要跳转的路径出现在了 Servlet 代码中,即可以使用后台路径。
注意,在 JSP 动作中,没有用于完成重定向的动作。
3.2 include动作
include 动作用于完成将指定页面包含到当前页面中的功能。
(1)用法
定义index.jsp页面
index page
<%
int sum=50;
%>
<br>
<jsp:include page="/left.jsp"/>
<br>
index page sum = <%=sum %>
定义left.jsp页面
left page
<%
int sum = 100;
%>
<br>
left page sum = <%=sum %>
运行结果:
index page
left jsp page
left page sum = 100
index page sum = 50
(2)动态联编
打开 Tomcat 的 work 目录的相关子目录,可以看到有两个.java 文件:index_jsp.java 与left_jsp.java。也就是说,包含动作的包含,是在运行期完成的,而非在编译期。这个包含动作,是在程序运行过程中,由 index_jsp 文件中的_jspService()方法通过 JspRuntimeLibrary 类
的 include()方法调用了 left_jsp 文件中的_jspService()方法。在运行期所执行的这种包含,称为动态联编。
(3)静态联编与动态联编的应用场景
在静态联编与动态联编均可使用时,一般使用静态联编。因为在程序运行时只存在一个Servlet,对资源的消耗较少,且不存在调用问题,执行效率较高。若在两个文件间需要共享同一变量,此时只能使用静态联编。若在两个文件间存在同名变量,且不能混淆,此时只能使用动态联编。