什么是JSP?
JSP就是Java Server Pages,就是通过在标准的HTML页面中嵌入Java代码。整个页面由两部分组成:
-
静态部分:标准的HTML标签、静态的页面内容,这些内容与静态HTML页面相同
-
动态部分:受Java程序控制的内容,这些内容由Java程序来动态生成
为什么要用JSP?
-
在Servlet中嵌入大量的静态文本格式,导致Servlet的开发效率低,代码难维护,而JSP就是为了解决这个问题而出现的
-
虽然现在有很多技术已经可以替代JSP了,尤其是一些创业公司和互联网公司,但是在JavaEE开发中还是经常用到的
-
用Java开发Web程序,JSP算是入门级的
JSP本质
JSP的本质还是Servlet,每个JSP页面就是一个Servlet实例。JSP页面由系统编译成Servlet,Servlet再负责响应用户请求。也就是说,JSP是Servlet的另外一种形式,使用JSP时,其实还是使用Servlet,因为Web应用中的每个JSP页面都会由Servlet容器生成对应的Servlet,比如Tomcat,最终都是由对应的Servlet来处理用户的请求。
JSP注释
从最基本的注释说起。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<
html
>
<
head
>
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=utf-8"
>
<
title
>各种注释</
title
>
</
head
>
<
body
>
<%-- 这里是JSP注释 --%>
<%
/**
*这个是Java多行注释
*/
for (int i = 0; i < 3; ++i)
{
out.println("http://inslow.com"); // 这个是Java单行注释
}
%>
<!-- 这个是HTML注释 -->
</
body
>
</
html
>
|
HTML的注释可以通过源码查看到,但JSP的注释是无法通过网页源码查看到的,也就是说JSP注释不会被发送到客户端。JSP注释跟Java注释一样都是给程序猿看的。
JSP声明
JSP声明用于声明变量和方法。在JSP中,我们似乎不会看到的类的直接定义,方法貌似可以独立存在,这好像有点脱离了Java的轨迹。实际上,JSP声明将会被转换成对应的Servlet的成员变量或成员方法,所以JSP声明依然是遵循着Java语法的。
JSP的声明语法格式如下:
1
|
<%! 声明部分 %>
|
例如:
1
2
3
4
5
6
|
<%!
int i = 0;
String getDefaultWebsite(){
return "http://inslow.com";
}
%>
|
我们可以在生成对应的Java源码文件中找到以下定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
final
class
greeting_jsp
extends
org.apache.jasper.runtime.HttpJspBase
implements
org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
// 变量i对应在JSP中定义的i
private
int
i =
0
;
// 函数getDefaultWebsite对应JSP中的getDefaultWebsite
public
String getDefaultWebsite()
{
return
"http://inslow.com"
;
}
// ..............
}
|
可以很直观的看到,JSP页面的声明部分被转换成了对应Servlet的成员变量或成员方法。
JSP表达式
JSP提供了一种输出表达式值的简单方法,可以输出各种类型数据,具体包括int、double、boolean、String、Object等,具体格式如下:
1
|
<%= 表达式%>
|
例如:
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
|
<%!
private
int
i =
0
;
private
String defaultSite =
"http://inslow.com"
;
private
boolean
isFinished =
true
;
private
double
money =
23
.45f;
public
String toString(){
return
"输出JSP表达式测试类"
;
}
%>
<%-- 输出
int
类型值 --%>
<%= i%>
<%-- 输出String类型值 --%>
<%= defaultSite %>
<%-- 输出
boolean
类型值 --%>
<%= isFinished %>
<%-- 输出
double
类型值 --%>
<%= money %>
<%-- 输出Object类型值 --%>
<%=
this
%>
|
注意:
1、当输出Object类型的值时,会调用对应的toString方法。
2、虽然表达式符合Java语言规范,但是表达式不能用分号(“;”)来结束。
JSP脚本
在JSP页面中可以包含任何可以执行的Java代码,所有可执行Java代码都可以通过如下方式嵌入JSP页面中,
1
|
<% Java代码 %>
|
例如:
1
2
3
4
5
6
7
8
9
|
<%
for
(
int
i =
0
; i <
3
; ++i){
out.println(
"Pazha | inslow.com"
);
}
%>
<br/>
<%
out.println(
"重要的事情说三遍"
);
%>
|
JSP指令
JSP指令用来声明JSP页面的一些属性,例如编码格式、文档类型等。这些指令用来告知JSP引擎如何处理该JSP页面。经常使用的编译指令有以下三个:
-
page指令:该指令是针对当前页面属性的指令;
-
include指令:用于指定包含另一个页面;
-
taglib指令:用于定义和访问自定义标签。
使用编译指令的语法格式如下:
1
|
<%@ 编译指令名 属性名=“属性值”...%>
|
现在就分别对以上三个编译指令进行总结。
page指令:
page
指令是最常用的指令,用来声明JSP页面的属性等。比如:
1
|
<%@ page language="java" contentType="text/html"; charset="utf-8" %>
|
需要注意的是,任何page
允许的属性都只能出现一次,否则会出现编译错误;import
属性除外,import
可以多次出现。
我们可以在page
指令中设置以下的属性:
属性名称 | 取值范围 | 描述 |
---|---|---|
language | java | 指定解释该JSP文件时采用的语言,默认为java |
extends | 任何类的全名(包含包名) | 指定JSP页面编译所产生的Java类所继承的父类,或所实现的接口 |
import | 任何类名、包名 | 引入该JSP中用到的类、包等。JSP会默认导入四个包:java.lang.* 、javax.servlet.* 、javax.servlet.jsp.* 、javax.servlet.http |
session | true、false | 指明该JSP页面是否内置Session对象。如果为true,则内置Session对象;否则不内置Session对象。默认为true |
buffer | none、数字+kb | 指定缓存大小。当autoFlush为true时有效 |
autoFlush | true、false | 是否运行缓存。如果为true,则使用out.println() 等方法输出的字符串并不是立刻到达客户端的,而是暂存在缓存中;当缓存满、程序执行完毕或者执行out.flush() 操作时才到客户端。默认为true |
isThreadSafe | true、false | 用来设置JSP页面是否可以多线程访问。当设置为true时,JSP页面能同时响应多个客户的请求;当设置为false时,JSP页面同一时刻只能响应一个客户的请求,其他客户需要排队等待。默认值为true |
info | 任意字符串 | 指明JSP的信息。该信息可以通过Servlet.getServletInfo() 方法获得 |
errorPage | 某个JSP页面的相对路径 | 指明一个错误显示页面。如果该JSP程序抛出了一个未捕获的异常,则转到errorPage指定的页面。errorPage指定的页面通常isErrorPage属性为true,且内置的exception对象为未捕获的异常 |
contentType | 合法的文档类型 | 客户端根据该属性判断文档类型,具体的请参见这里 |
pageEncoding | 指定生成网页的编码字符集 | JSP文件本身的编码,将JSP翻译成Java源码时,就是根据pageEncoding的编码格式读取的 |
isErrorPage | true、false | 设置JSP页面是否为错误处理页面;如果该页面本身已是错误处理页面,则通常无须指定errorPage属性 |
include指令:
使用include
指令,可以将一个外部文件嵌入到当前的JSP文件中。编译时,当前的JSP文件完全包含了被包含页面的代码。举个例子说明:
页面一(page1.jsp)主要代码:
1
2
3
4
5
6
7
8
|
<%@ page language="java" contentType="text/html; charset=utf-8"%><%@ page pageEncoding="UTF-8"%>
<
body
>
<%
out.println("这是页面一");
%>
<%-- 这里包含页面二 --%>
<%@ include file="page2.jsp" %>
</
body
>
|
页面二(page2.jsp)主要代码:
1
2
|
<%@ page pageEncoding="UTF-8"%>
<%out.println("这是页面二");%>
|
运行该程序,在生成class和java目录下,我们都无法找到page2_jsp.class和page2_jsp.java。这说明页面二还未经过编译就已经添加到页面一中了,这就好比直接将页面二的代码写到页面一中,请记住这一点,这将和后面总结到的include动作是截然相反的原理。
taglib指令:
JSP支持标签技术,使用标签功能可以实现视图代码重用,很少量的代码就能实现很复杂的显示效果。由于taglib指令
是一项非常重要的技术,我们可以自定义我们自己的标签库,后续总结自定义标签库中在一起总结taglib
指令,这里就不废话了。
JSP动作指令
JSP中的动作与JSP指令不同,指令是告知Servlet引擎如何编译当前JSP页面,而动作指令只是运行时的动作。JSP动作是对常用的JSP功能的抽象与封装,它只是JSP脚本的标准化写法。指令影响编译,动作影响运行。
JSP动作主要有以下七个:
-
jsp:forward:执行页面转向,将请求的转发到下一个页面
-
jsp:param:用于传递参数,必须与其它支持参数的标签一起使用
-
jsp:include:用于动态引入一个JSP页面,注意与
include指令
进行区别 -
jsp:plugin:用于下载Applet到客户端执行,现在已经基本放弃这种东西了
-
jsp:useBean:创建一个JavaBean的实例
-
jsp:setProperty:设置JavaBean实例的属性值
-
jsp:getProperty:获取JavaBean实例的属性值
下面就对上述的七种JSP动作分别进行总结。
jsp:forward动作
jsp:forward
动作用于将页面响应转发到另外的页面。可以转发到静态的HTML页面,也可以转发到动态的JSP页面,还可以转发到Servlet。
jsp:forward
动作的语法如下:
1
2
3
|
<
jsp:forward
page
=
"relativeURL"
>
<
jsp:param
name
=
""
value
=
""
/>
</
jsp:forward
>
|
<jsp:param>
用来增加请求参数,我们可以在目的页面使用HttpServletRequest
类的getParameter()
方法获取。下面通过一个例子来说明,如何从页面一转到页面二。
页面一(page1.jsp)主要代码:
1
2
3
4
5
6
7
8
|
<
body
>
<%
out.println("这是页面一");
%>
<
jsp:forward
page
=
"page2.jsp"
>
<
jsp:param
name
=
"site"
value
=
"http://inslow.com"
/>
</
jsp:forward
>
</
body
>
|
页面二(page2.jsp)主要代码:
1
2
3
4
5
6
7
|
<
body
>
<%
out.println("这是页面二");
%>
<
br
/>
<%= request.getParameter("site") %>
</
body
>
|
运行程序以后,输出如下:
1
|
这是页面二 http://.com
|
可以看到并没有输出:这是页面一。这是由于从表面看,<jsp:forward>
动作是将用户请求“转发”到了另一个新页面;而实际上,<jsp:forward>
并没有重新向新页面发送请求,它只是完全采用了新页面代替原有页面来对用户生成响应——请求依然是一次请求,所以之前的请求参数、请求属性都不会丢失,而是都“转到”了新的页面。但是,浏览器的URL并不会因为<jsp:forward>
而发生改变。
jsp:param动作
jsp:param
动作用于设置参数值。由于单独的jsp:param
动作没有实际意义,所以这个动作本身不能单独使用,而是需要结合以下三个动作一起使用:
-
jsp:include
-
jsp:forward
-
jsp:plugin
当与jsp:include
动作指令结合使用时,jsp:param
动作用于将参数值传入被导入的页面;当与jsp:forward
动作结合使用时,jsp:param
动作用于将参数传入被转向的新页面;当与jsp:plugin
结合使用时,则用于将参数传入页面的JavaBean实例或Applet实例。
jsp:include动作
jsp:include
动作用于包含某个页面,但是它不会导入被jsp:include
页面的编译指令,仅仅将被导入页面的body部分的内容插入当前页面。
jsp:include
动作的语法格式如下:
1
2
3
|
<
jsp:include
page
=
"relativeURL"
flush
=
"true"
>
<
jsp:param
name
=
""
value
=
""
/>
</
jsp:include
>
|
flush
属性用于指定输出缓存是否转移到被导入的文件中;如果指定为true,则包含在被导入文件中;如果为false,则包含在原文件中。通过一个简单的例子来说明问题:
页面一(page1.jsp)主要代码:
1
2
3
4
5
6
7
8
|
<
body
>
<%
out.println("这是页面一");
%>
<
jsp:include
page
=
"page2.jsp"
>
<
jsp:param
name
=
"website"
value
=
"http://inslow.com"
/>
</
jsp:include
>
</
body
>
|
页面二(page2.jsp)主要代码:
1
2
3
4
5
6
|
<
body
>
<%
out.println("这是页面二");
%>
<%= request.getParameter("website") %>
</
body
>
|
运行程序,我们在Tomcat的work目录下会发现page1_jsp.class和page2._jsp.class,从这里就可以看出和指令<%@ include %>
的区别,动作<jsp:include >
是在编译成class以后再进行include的,我们可以在page1_jsp.java中发现这样一行代码:
1
2
3
4
5
6
7
8
|
org.apache.jasper.runtime.JspRuntimeLibrary.include
(request, response,
"page2.jsp" +
"?" +
org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("website", request.getCharacterEncoding())+
"=" +
org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("http://inslow.com", request.getCharacterEncoding()),
out, false);
|
在page1中是通过调用include
方法来包含page2的输出的。
jsp:plugin动作
这种已经真的退出历史舞台的东西,直接pass掉了。如果你还感兴趣,直接去Google吧。
jsp:useBean动作
首先需要明白Java Bean就是普通的Java类,也叫做POJO(Plain Ordinary Java Object,普通Java对象),不要被这些名词所迷惑了。其次,以下三个动作都是与Java Bean相关联的:
-
jsp:useBean动作
-
jsp:setProperty动作
-
jsp:getProperty动作
<jsp:useBean>
动作用于在JSP中定义一个Java Bean对象和初始化这个Java实例,具体的语法格式为:
1
|
<
jsp:useBean
id
=
"beanId"
class
=
"className"
scope
=
"Value"
/>
|
属性名 | 取值范围 | 描述 |
---|---|---|
id | 合法的Java变量名称 | 指明Java Bean对象的名称;在JSP中可以使用该名称引用该Java Bean对象 |
class | Java Bean类的全名 | Java Bean类的全名,包含包名 |
scope | 可以取值为:page、request、session和application | page:该JavaBean实例仅在该页面有效;request:该JavaBean实例在本次请求有效;session:该JavaBean实例在本次会话内有效;application:该JavaBean实例在本应用内一直有效 |
有的时候,我们经常被scope这样的作用域搞晕了,在后面的文章中,将会再具体的总结page
、request
、session
和application
这四个作用域。
下面通过一个简单的实例来演示jsp:useBean
动作:
定义一个简单的Java类:
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
|
package
com.jellythink.practise;
public
class
PersonInfo{
private
String name;
private
int
age;
private
String sex;
public
String getName()
{
return
name;
}
public
void
setName(String name)
{
this
.name = name;
}
public
int
getAge()
{
return
age;
}
public
void
setAge(
int
age)
{
this
.age = age;
}
public
String getSex()
{
return
sex;
}
public
void
setSex(String sex)
{
this
.sex = sex;
}
}
|
页面page1.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
|
<
body
>
<
form
action
=
"page2.jsp"
method
=
"get"
>
<
fieldset
>
<
legend
align
=
"center"
>请填写个人信息</
legend
>
<
table
align
=
"center"
width
=
"400"
>
<
tr
>
<
td
>姓名</
td
>
<!-- 这里的name值需要和Java类中的属性值进行对应 -->
<
td
><
input
type
=
"text"
name
=
"name"
value
=
""
/></
td
>
</
tr
>
<
tr
>
<
td
>年龄</
td
>
<
td
><
input
type
=
"text"
name
=
"age"
value
=
""
/></
td
>
</
tr
>
<
tr
>
<
td
>性别</
td
>
<
td
>
<
input
type
=
"radio"
name
=
"sex"
value
=
"Male"
/>Male
<
input
type
=
"radio"
name
=
"sex"
value
=
"Female"
/>Female
</
td
>
</
tr
>
<
tr
>
<
td
></
td
>
<
td
><
input
type
=
"submit"
name
=
"submit"
value
=
"提交"
class
=
"button"
/></
td
>
</
tr
>
</
table
>
</
fieldset
>
</
form
>
</
body
>
|
页面page2.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
37
|
<
body
>
<%-- 实例化PersonInfo类 --%>
<
jsp:useBean
id
=
"personInfo"
class
=
"com.jellythink.practise.PersonInfo"
scope
=
"page"
/>
<%-- 设置所有属性,也可以设置单个属性 --%>
<
jsp:setProperty
name
=
"personInfo"
property
=
"*"
/>
<
form
>
<
fieldset
>
<
legend
align
=
"center"
>您填写的个人信息</
legend
>
<
table
align
=
"center"
width
=
"400"
>
<
tr
>
<
td
>姓名</
td
>
<%-- 取属性值 --%>
<
td
><
jsp:getProperty
name
=
"personInfo"
property
=
"name"
/></
td
>
</
tr
>
<
tr
>
<
td
>年龄</
td
>
<
td
><
jsp:getProperty
name
=
"personInfo"
property
=
"age"
/></
td
>
</
tr
>
<
tr
>
<
td
>性别</
td
>
<
td
><
jsp:getProperty
name
=
"personInfo"
property
=
"sex"
/></
td
>
</
tr
>
<
tr
>
<
td
></
td
>
<
td
><
input
type
=
"button"
name
=
"back"
value
=
"返回"
class
=
"button"
onclick
=
"history.go(-1)"
/>
</
td
>
</
tr
>
</
table
>
</
fieldset
>
</
form
>
</
body
>
|
jsp:setProperty动作
jsp:setProperty
动作的语法格式为:
1
|
<
jsp:setProperty
name
=
"Bean Name"
property
=
"propertyName"
value
=
"value"
/>
|
当使用通配符”*”的时候,就需要页面中的属性name和Java类中的属性名称相匹配。具体请参见上述的代码例子。
jsp:getProperty动作
jsp:getProperty动作
动作的语法格式为:
1
|
<
jsp:getProperty
name
=
"Bean Name"
property
=
"propertyName"
/>
|
具体请参见上述的代码例子。