3.6.3 include动作

include动作用来动态地引入另一个资源。可以引入另一个JSP页面,也可以引入一个Servlet或一个静态的HTML页面。

实例

copyright2.jsp:

1
2
3
4
5
<%@ page contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<hr />
&copy;2018 一个麻瓜的网站
<hr />

jspIncludeTest.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Include action</title>
</head>
<body>
<h2>测试include指令</h2>
<jsp:include page="copyright2.jsp">
<jsp:param name="text" value="How are you?" />
</jsp:include>
</body>
</html>

浏览器显示效果:
这里有一张图片

include指令和include动作的区别

理解include指令和include动作非常重要。

  • 第一个不同的是,对于**include指令,资源的引入发生在页面转换时,即当JSP容器将页面转换为Servlet的时候。而对于include动作,资源的引入发生在请求页面时。因此,使用include动作可以传递参数,而使用include指令无法传递参数。**
  • 第二个不同的是,include指令对引入的文件扩展名没有特殊要求。但对于include动作,若引入的文件需要以JSP页面进行处理,则其文件扩展名必须是JSP。若使用.jspf为扩展名,则该页面将被当作静态文件

3.6.2 setProperty和getProperty

setProperty动作可对一个Java对象的属性设置属性值,而getProperty动作则会输出Java对象的一个属性的属性值

实例

Person类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package app03a;
public class Person
{
private String id;
private String firstName;
private String lastName;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
}

getSetPropertyTest.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>getProperty and setProperty</title>
</head>
<body>
<%-- 创建一个app03a.Person类的对象,并把它保存在脚本变量employee中 --%>
<jsp:useBean id="employee" class="app03a.Person" />
<%-- 设置id为employee这个脚本变量所对应的对象的firstName成员变量的值为Blue --%>
<jsp:setProperty name="employee" property="firstName" value="Blue" />
First Name:
<%-- 取出id为employee这个脚本变量所对应的对象的firstName成员变量的值 --%>
<jsp:getProperty name="employee" property="firstName" />
</body>
</html>

访问这个JSP,显示效果如下:
这里有一张图片
打开JSP页面转换成的getSetPropertyTest_jsp.java这个Servlet中的_jspService方法.可以看到上面的JSP页面被转换成如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<title>getProperty and setProperty</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\r\n");
out.write("\t");
app03a.Person employee = null;
employee = (app03a.Person) _jspx_page_context.getAttribute(
"employee", javax.servlet.jsp.PageContext.PAGE_SCOPE);
if (employee == null){
employee = new app03a.Person();
_jspx_page_context.setAttribute(
"employee", employee, javax.servlet.jsp.PageContext.PAGE_SCOPE);
}
out.write('\r');
out.write('\n');
out.write(' ');
out.write('\r');
out.write('\n');
out.write(' ');
org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper(
_jspx_page_context.findAttribute("employee"), "firstName", "Blue", null, null, false);
out.write("\r\n");
out.write("\tFirst Name:\r\n");
out.write("\t");
out.write('\r');
out.write('\n');
out.write(' ');
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString(
(((app03a.Person)_jspx_page_context.findAttribute("employee")).getFirstName()))
);
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>");

3.6.1 useBean

useBean将创建一个脚本变量,这个脚本变量关联一个Java对象。然后访问这个脚本变量就等同于访问这个Java对象.
这是早期分离表示层和业务逻辑的手段。随着其他技术的发展,如自定义标签和表达语言,现在已经很少使用useBean方式了

实例

useBeanTest.jsp:

1
2
3
4
5
6
7
8
<html>
<head>
<title>useBean</title>
</head>
<body><jsp:useBean id="today" class="java.util.Date"/>
<%=today%>
</body>
</html>

查看Tomcat转换后的Servlet中的_jspService方法,可以看到上述代码会被转换为如下代码:

1
2
3
4
5
6
7
8
9
10
11
java.util.Date today = null;
today = (java.util.Date) _jspx_page_context.getAttribute("today",
javax.servlet.jsp.PageContext.PAGE_SCOPE);
if (today == null) {
today = new java.util.Date();
_jspx_page_context.setAttribute("today", today,
javax.servlet.jsp.PageContext.PAGE_SCOPE);
}
......
out.print(today);
......

访问这个页面,可以看到当前的日期和时间。
这里有一张图片

3.6 动作

动作是第三种类型的语法元素动作将被转换成Java代码来执行操作,例如访问一个Java对象或调用方法。本节仅讨论所有JSP容器支持的标准动作。除标准外,还可以创建自定义标签执行某些操作。
下面将介绍一些标准的动作。doBodyinvoke的标准动作会在第7章 Tag文件中详细讨论。

3.5.3 禁用脚本元素

随着JSP 2.0表达式语言的加强,推荐的实践是:JSP页面中用EL来访问服务器端对象且不要写Java代码。因此,自JSP 2.0开始,可以通过在部署描述符中的<jsp-property-group>定义一个<scripting-invalid>元素,来禁用脚本元素
如下所示:

1
2
3
4
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>

3.5.2 声明

声明以<%!开始,并以%>结束。可以在JSP页面中声明一个变量或方法,以便在其他地方调用.

实例

declarationTest.jsp页面中声明了一个名为getTodaysDate的方法,如下所示

1
2
3
4
5
6
7
8
9
10
11
<%!
public String getTodaysDate() {
return new java.util.Date();
}
%>
<html>
<head><title>Declarations</title></head>
<body>
Today is <%=getTodaysDate()%>
</body>
</html>

声明可以出现的位置

JSP页面中,一个声明可以出现在任何地方,并且一个页面可以有多个声明。

使用声明来重写JSP页面

可以使用声明来重写JSP页面,实现类的initdestroy方法。具体做法是通过声明jspInit方法,来重写init方法。通过声明jspDestroy方法,来重写destroy方法。

jspInit和jspDestroy方法说明

这两种方法说明如下:

  • jspInit。这个方法类似于 javax.servlet.Servlet init方法。**JSP页面在初始化时调用jspInit**。不同于init方法,jspInit没有参数。还可以通过隐式对象config访问ServletConfig对象。
  • jspDestroy。这个方法类似于Servletdestroy方法,在JSP页面将被销毁时调用。

实例

lifeCycle.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%!
public void jspInit() {
System.out.println("jspInit ...");
}
public void jspDestroy() {
System.out.println("jspDestroy ...");
}
%>
<html>
<head><title>jspInit and jspDestroy</title></head>
<body>
Overriding jspInit and jspDestroy
</body>
</html>

lifeCycle.jsp页面会被转换成如下Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
* Generated by the Jasper component of Apache Tomcat Version: Apache
* Tomcat/8.5.35 Generated at: 2019-03-28 08:43:25 UTC Note: The last modified
* time of this file was set to the last modified time of the source file after
* generation to assist with modification tracking.
*/
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class lifeCycle_jsp
extends
org.apache.jasper.runtime.HttpJspBase
implements
org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports
{
public void
jspInit()
{
System.out.println("jspInit ...");
}
public void
jspDestroy()
{
System.out.println("jspDestroy ...");
}
private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory
.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long>
getDependants()
{
return _jspx_dependants;
}
public java.util.Set<java.lang.String>
getPackageImports()
{
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String>
getClassImports()
{
return _jspx_imports_classes;
}
public javax.el.ExpressionFactory
_jsp_getExpressionFactory()
{
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory
.getJspApplicationContext(
getServletConfig().getServletContext())
.getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager
_jsp_getInstanceManager()
{
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory
.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void
_jspInit()
{}
public void
_jspDestroy()
{}
public void
_jspService(final javax.servlet.http.HttpServletRequest request,
final javax.servlet.http.HttpServletResponse response)
throws java.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;
}
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.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');
out.write('\n');
out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<title>测试jspInit和jspDestroy</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("<h2>测试jspInit和jspDestroy</h2>\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.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 new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}

注意生成的Servlet类中的jspInitjspDestroy方法。现在可以用如下URL访问lifeCycle.jsp:
http://localhost:8080/app03a/lifeCycle.jsp
第一次访问页面时,可以在控制台上看到“jspInit...”,以及在Servlet/JSP容器关闭时看到“jspDestory...”。

3.5.1 表达式

表达式以<%=开始,并以%>结束。
每个表达式都会被JSP容器执行,并使用隐式对象out的打印方法输出结果

实例

例如:

1
Today is <%=java.util.Calendar.getInstance().getTime()%>

注意,表达式无须分号结尾

JSP容器处理表达式过程

JSP容器首先执行java.util.Calendar.getInstance().getTime(),并调用out.print()打印其计算结果。

等效的脚本程序

等效脚本程序如下所示:

1
2
3
4
Today is
<%
out.print(java.util.Calendar.getInstance().getTime());
%>

3.5 脚本元素

一个脚本程序就是一个Java代码块,脚本程序的开始符号为<%,结束符号为%>

实例

scriptletTest.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="java.util.Enumeration"%>
<html>
<head>
<title>Scriptlet实例</title>
</head>
<body>
<b>Http headers:</b>
<br />
<%-- first scriptlet --%>
<%
out.print("<table>");
for (Enumeration<String> e = request.getHeaderNames();
e.hasMoreElements();)
{
String header = e.nextElement();
out.println(
"<tr><td align='right'>" + header + ":</td><td>"
+ request.getHeader(header) + "</td></tr>");
}
out.print("</table>");
String message = "Hello World";
%>
<hr />
<%-- second scriptlet --%>
<%
out.println(message);
%>
</body>
</html>

上面JSP页面中有两个脚本程序,需要注意的是定义在一个脚本程序中的变量可以被其后续的脚本程序使用
脚本程序第一行代码可以紧接<%标记,最后一行代码也可以紧接%>标记,不过,这会降低代码的可读性。

3.4.2 include指令

include指令的作用

使用include指令可以把其他文件中的内容插入到当前的JSP页面之中。一个页面中可以有多个include指令。

使用场景

如果在多个不同页面中包含了一些相同的内容,或者在同一个页面不同位置中包含了一些相同的内容.则应该把这些相同的内容提取出来,单独保存在一个文件中,然后通过include指令来导入这个文件.因为如果后面需要修改这些相同的内容,只需要打开保存这些相同的内容所在的文件进行修改即可,不需要打开所有的文件进行修改。

include指令语法格式

include指令的语法如下:

1
<%@ include file="url"%>

其中,@include间的空格不是必须的,URL为被包含文件的相对路径,如果URL以一个斜杠(/)开始,则该URL为文件在服务器上的绝对路径,否则为当前JSP页面的相对路径。

JSP转换器如何处理include指令

JSP转换器处理include指令时,JSP转换器会找到include指令所指向的文件,并把文件中的内容插入到这条指令所在的位置.

实例

copyright.jspf:

1
2
3
4
5
<%@ page contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<hr />
&copy;2018 一个麻瓜的网站
<hr />

main.jsp中使用include指令导入copyright.jsp页面中的内容:

1
2
3
4
5
6
7
8
9
10
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><title>测试include指令</title></head>
<body>
下面是inclue指令所指向的文件中的内容:
<%@ include file="copyright.jspf"%>
</body>
</html>

显示效果:
这里有一张图片

等效写法

main.jsp页面中应用include指令和如下页面的效果是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><title>测试include指令</title></head>
<body>
下面是inclue指令所指向的文件中的内容:
<hr />
&copy;2018 一个麻瓜的网站
<hr />
</body>
</html>

如上示例中,为保证include指令能正常工作,copyright.jspf文件必须同main.jsp位于相同的目录。按照惯例,以JSPF为扩展名的文件代表JSP fragement。虽然JSP fragement现在被称为JSP segment,但为保证一致性,JSPF后缀名依然被保留。
注意,include指令也可以包含静态HTML文件。

3.4.1 page指令

可以使用page指令来控制JSP转换器转换当前JSP页面的某些方面。例如,可以告诉JSP用于转换隐式对象out的缓冲器的大小、内容类型,以及需要导入的Java类型,等等。

page指令语法格式

page指令的语法如下:

1
<%@ page attribute1="value1" attribute2="value2" ... %>

@page间的空格不是必须的,attribute1attribute2等是page指令的属性。

page指令属性列表

如下是page指令属性的列表:

  • import:定义一个或多个本页面中将被导入和使用的java类型。例如:import="java.util.List"将导入List接口。可以使用通配符“*”来引入整个包,类似import="java.util.*"。可以通过在两个类型间加入“,”分隔符来导入多个类型,如import="java.util.ArrayList、java.util.Calendar、java.io.PrintWriter"。此外,JSP默认导入如下包:java.langjavax.servletjavax.servlet.httpjavax.servlet.jsp
  • session:如果session属性值为True,则在本页面之中加入会话管理;如果值为False则不加入会话管理。默认值为True,访问该页面时,若当前不存在javax.servlet.http.HttpSession实例,则容器会创建一个。
  • buffer:以KB为单位,定义隐式对象out的缓冲大小。必须以KB后缀结尾。默认大小为8KB或更大(取决于JSP容器)。该值可以为none,这意味着没有缓冲,所有数据将直接写入PrintWriter
  • autoFlush:默认值为True。若值为True,则当输出缓冲满时会自写入输出流。而值为False,则仅当调用隐式对象的flush方法时,才会写入输出流。因此,若缓冲溢出,则会抛出异常。
  • isThreadSafe:定义该页面的线程安全级别。不推荐使用该参数,因为使用该参数后,会生成一些Servlet容器已过期的代码。
  • info:返回容器生成的Servlet类的getServletInfo方法的结果。
  • errorPage:定义当出错时用来处理错误的页面。
  • isErrorPage:标识本页是一个错误处理页面。
  • contentType:**定义本页面隐式对象response的内容类型,默认是text/html**。
  • pageEncoding:**定义本页面的字符编码,默认是ISO-8859-1**。
  • isELIgnored:配置是否忽略EL表达式。ELExpression Language的缩写。
  • language:**定义本页面的脚本语言类型,默认是Java**,这在JSP 2.2中是唯一的合法值。
  • extends:定义JSP的实现类要继承的父类。这个属性的使用场景非常罕见,仅在非常特殊情况下使用
  • deferredSyntaxAllowedAsLiteral:定义是否解析字符串中出现{#这个符号,默认是False{#是一个表达式语言的起始符号。(这个地方可能有错,后面再验证.)
  • trimDirectiveWhitespaces:定义是否不输出多余的空格/空行,默认是False

page指令可以出现的位置

大部分page指令可以出现在页面的任何位置,但page指令包含contentTypepageEncoding属性时,其必须出现在Java代码发送任何内容之前。这是因为内容类型和字符编码必须在发送任何内容前设定。

page指令import属性

page指令也可以出现多次,但出现多次的指令属性必须具有相同的值。不过,import属性例外,多个包含import属性的page指令的结果是累加的。例如,以下page指令将同时导入java.util.ArrayListjava.util.Date类型:

1
2
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.Date"%>

这和如下的写法,效果是一样的:

1
<%@page import="java.util.ArrayList, java.util.Date"%>

page指令可以同时设置多个属性

一个page指令可以同时有多个属性。下面的代码设定了session属性和buffer属性:

1
<%@page session="false" buffer="16kb"%>