JSP核心
- jsp:
demo1.jsp
<%
session.setAttribute( "user","bjpowernode" );
%>
- jsp:
demo2.jsp
<%
String user = (String)session.getAttribute( "user" );
%>
user = <%=user %>
2.1 JSP的内置对象
概念: 在JSP
的Java
代码块、表达式块等中可以直接使用的引用,称为JSP
的内置对象。常用的内置对象有九个,分别是:
序号 | 内置对象名(引用名) | 类型 |
---|---|---|
1 | pageContexte | javax.servlet.jsp.PageContext |
2 | request | javax.servlet.http.HttpServletRequest |
3 | session | javax.servlet.http.HttpSession |
4 | application | javax.servlet.ServletContext |
5 | response | javax.servlet.Http.HttpServletResponse |
6 | config | javax.servlet.ServletConfig |
7 | out | javax.servlet.jsp.JspWrite*r |
8 | page | java.long.Object |
9 | exception | java.long.Throwable |
- 这九个对象在
JSP
的Java
代码块、表达式块中可以直接使用。只所以可以直接使用,是因为Java
代码块与表达式块被JSP
引擎翻译后均出现在了Servlet
的_jspService()
方法中。而这九个对象,就是_jspService()
方法中的局部变量。在JSP
的Java
代码块、表达式块中的代码就是_jspService()
方法中的代码,所以在其中可以直接使用。
2.1.1 pageContexte
概念:
pageContext
,页面上下文,其具有一个只在当前页面范围的域属性空间,即其具有setAttribute()
方法与getAttribute()
方法。但,由于在当前页面范围,数据不存放到域属性空间也可直接使用,将数据存放到域属性空间反而感觉“多此一举”,所以这些方法并不常用。不过,在同一页面中,为了使用EL
表达式(后面学习)来访问某变量的值,此时一般使用pageContext
。pageContext
具有一些get
方法,可以获取到Request
,Response
、Session
,ServletContext
、servletConfig
、page
(即当前Servlet
)、exception
、out
等另外八个内置对象。- 但由于这些对象本身就是内置对象,在
JSP
页面中可以直接使用,所以这些方法也并不常用。不过,在后面将要学习的EL
表达式中,将会经常使用pageContext
的这些方法。
- 但由于这些对象本身就是内置对象,在
<%
pageContext.setAttribute( "xxx", "aaa");
%>
<%
String xxx = (String)pageContext.getAttribute( "xxx" );
%>
2.1.2 application
-
application
,即servletContext
。所以servletContext
所具有的方法application
都具有。常用的方法例如, -
String getInitParameter()
: 获取web.xml
文件的<context-param/>
中指定名称的上下文参数值。例如getInitParameter("myDBDriver")
;会返回字符串“com.mysql.jdbc.Driver”
。 -
Enumeration getInitParameterNames()
: 获取web.xml
文件的<context-param/>
中的所有的上下文参数名称。其返回值为枚举类型Enumeration<String>
-
void setAttribute(String name, Object object)
: 在servletContext
的公共数据空间中,也称为域属性空间,放入数据。这些数据对于web
应用来说,是全局性的,与整个应用的生命周期相同。当然,放入其中的数据是有名称的,通过名称来访问该数据。 -
Object getAttribute(String name)
:从ServletContext
的域属性空间中获取指定名称的数据。 -
void removeAttribute(string name)
:删除指定名字的属性
2.1.3 out
out
,类型为javax.servlet.jsp.JspWriter
。查看JavaEE
文档,发现JspWriter
类继承自IO
流的Writer
类。即out
就是一个输出流对象。
<%
int sum = 5;
out.print( "sum = " +sum) ;
%>
2.1.4 page
- 查看
JSP
翻译为的Servlet
,可以看到page
对象即servlet
对象本身。这个对象在实际应用中并不常用。
2.1.5 exception
- 在普通的
JSP
页面中是不能使用exception
内置对象的。因为打开JSP
翻译为的Servlet
,发现其中并没有excebtion
对象。若要在页面中直接使用exception
对象,则需要配合着page
指令使用。page
指令后面讲解。
2.1.6 其他对象
- 其它对象,还有
request
、response
、session
,及config
。它们的用法与之前Servlet
学习时的用法相同。只不过是直接使用在了JSP
页面中了。
2.2 JSP指令(directive)
概念: JSP
指令的作用是为当前页面做一些基本的属性设置,为当前的页面的运行提供基本的环境。
- 根据功能的不同,
JSP
中包含三类指令:page
指令,即页面指令;include
指令,即包含指令﹔及taglib
指令,即标签库指令。无论哪种指令,其使用的语法格式均为如下形式:<%@ 指令名称 属性名=属性值 属性名=属性值 ....... %>
2.2.1 page指令
概念: page
指令用于设置当前JSP
页面的相关信息。一个JSP
文件中可以包含多个page
指令。常用的page
指令的属性意义及用法如下:
pageEncoding
:设置jsp页面所使用的字符编码格式,比如<%@ page pageEncoding="utf-8" %>
contentType
:比如<%@ page contentType="text/html; charset=UTF-8" %>
import
:用于完成在JSP页面导入指定的类,比如<%@ page import="java.util.Date" %>
isErrorPage
:控制由JSP
页面生成的servlet
是允许并行访问(默认),还是同一时间不允许多个请求访问单个servlet
实例(isThreadSafe="false")。extends
:指定JSP
页面所生成的servlet
的超类(superclass)- 比如
<%@ page extends="package.class" %>
- 比如
info
:定义一个可以在servlet
中通过getServletInfo
方法获取的字符串- 比如
<%@ page info="Some Message" %>
- 比如
2.2.2 include指令
概念: include
指令,即包含指令,用于将指定的文件包含到当前的JSP
文件中。该指令只有一个属性file
,用于指定要包含的文件。
- (1): 被
include
指定包含的文件,可以是JSP
动态页面文件,也可以是HTML静态页面文件。这里定义一个名为left.jsp
的动态文件。其中定义了一个变量sum
。
- (2): 静态联编
- 查看
Tomcat
的work
目录中相关子目录,发现只生成了一个index_jsp.java
的servlet
源文件,并没有生成left_jsp.java
文件。那是因为JSP
翻译引擎在翻译时,会将include
指令所指定的文件内容直接翻译到当前JSP
对应的Servlet
中,形成一个.java
文件。这就说明一个问题:这个包含操作是在编译之前完成的,是在编译之前由JSP
翻译引擎完成的,不是在程序运行期完成的。这种包含是一种静态包含,称为静态联编。 - 由于在编译期就将这些文件合并为了一个
Servlet
文件,所以,整个过程就一个_jspService()
方法。也就是说,这些文件之间是可以相互访问局部变量的。只要满足变量声明与使用的先后顺序即可。
2.3 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
属性,用于指定要转向的页面。
2.3.1 forward动作
- 页面中一旦具有了
forward
动作,那么当前页面中的所有要显示的内容都将无法显示。因为页面直接转发到了下一个页面。
2.3.2 include动作
- (1):
include
动作元素可以在JSP
页面中动态包含一个文件,文件的内容可以是静态的文件也可以是动态的脚本,而且当包含的动态文件被修改的时候JSP
引擎可以动态对其进行编译更新。 语法:<jsp:include></jsp:include>
- (2): 动态联编
- 打开
Tomcat
的work
目录的相关子目录,可以看到有两个.java
文件:index_jsp.java
与left_jsp.java
。也就是说,包含动作的包含,是在运行期完成的,而非在编译期。这个包含动作,是在程序运行过程中,由index_jsp
文件中的_jspService()
方法通过JspRuntimeLibrary
类的include()
方法调用了left_jsp
文件中的_jspService()
方法。在运行期所执行的这种包含,称为动态联编。 - (3): 静态联编与动态联编的应用场景
- 在静态联编与动态联编均可使用时,一般使用静态联编。因为在程序运行时只存在一个
Servlet
,对资源的消耗较少,且不存在调用问题,执行效率较高。 - 若在两个文件间需要共享同一变量,此时只能使用静态联编。
- 若在两个文件间存在同名变量,且不能混淆,此时只能使用动态联编。
2.4 EL表达式
概念: EL,Expression Language
,表达式语言,是一种在JSP
页面中获取数据的简单方式。EL
表达式是从JSP2.0
版本开始才引入的概念。EL表达式的基本语法形式很简单:在lSP页面的任何静态部分均可通过${expression}
的形式获取到指定表达式的值。
2.4.1 获取数据
- (1): 从四大域中依次查找数据
EL
只能从pageConext
、request
、session
、application
四大域属性空间中获取数据。以下方式是无法获取到指定数据的。因为这个数据没有存放在四大域属性空间中。
<%
int sum = 50;
%>
<!--这个数据sum是取不出来的。
因为其没有在四大域属性空间中 -->
sum =${sum }<br>
- (2): 从指定域中获取数据
- 从
pageContext
依次查找到application
域空间,会降低扰行效率。若某属性确定存放在某个域属性空间,则可指定直接从该空间中查找。此时需要借助EL
的四个域属性空间相关的内置对象。
- 从
序号 | 内置对象 | 说明 |
---|---|---|
1 | pageScope | 从page 范围域属性空间中查找指定的key |
2 | resultScope | 从result 范围域属性空间中查找指定的key |
3 | sessionScope | 从session 范围域属性空间中查找指定的key |
4 | applicationScope | 从application 范围域属性空间中查找指定的key |
<%
pageContext.setAttribute( "address","中国北京");
request.setAttribute( "address","大兴区");
session.setAttribute( "address","亦庄大族企业湾");
application.setAttribute( "address","10号楼A座");
%>
address = ${address } <br>
address = ${address } <br>
- (3): 访问Bean的属性
- EL可以通过
${key.属性}
的方式获取到指定对象的指定属性值。其底层实际调用的是该对象的相应属性的get方法。 - 当然,也可以使用
${key['属性'}
或${key["属性"}
的方式获取。该方式不常用。
- EL可以通过
<%
Student student = new Student("张三",23);
request.setAttribute( "stu", student);
%>
studentName = ${requestScope.stu.name }<br>
studentName = ${requestScope.stu['name'] }<br>
studentName = ${requestScope.stu["name"] j<br>
- 当然,若要访问一个对象的域属性的值,则可多次使用点号运算符,依次取出相应的属性值。
- 首先定义一个类:
<%@ page pageEncoding="UTF-8" import="com.yap.beans.Student"%>
<%
Student student = new Student("张三",23);
pageContext.setAttribute( "stlident", student);
%>
student = ${student } <br>
name =${student.name } <br>
age = ${student.age } <br>
name =${student['name'] }<br>
age = ${student['age'] }<br>
<!-- 若访问为null的对象的属性,EL是不会抛出空指针异常的,其仅仅是不显示而已 -->
name5 = ${student5.name } <br>
- (4): 获取数组中的元素
EL
可以通过${key[索引}
的方式获取到指定索引的元素。不过,需要注意的是,若数组中不存在该指定索引的元素,系统并不会抛出数组越界异常。
<%
String[] names = {"张三","李四","王五"};
pageContext.setAttribute("names", names);
%>
names[1]= ${names[1] }<br>
<!--若访问的数组元素下标超出了数组下上限,EL不会抛出越界异常-->
names[5] = ${names[5] }<br>
<%
School[] schools = new School[3];
schools[0] = new School("清华大学","中国北京");
schools[1] = new School("北京大学","中国北京");
schools[2] = new Schoo1("中国人民大学","中国北京");
pageContext.setAttribute( "schools", schools);
%>
schools[2].sname = ${schools[2].sname } <br>
- (5): 获取
list
中的元素- 与获取数组中的元素相同,通过
${key[索引}
的方式可以获取List
中指定索引的元素。若List
中不存在该指定索引的元素,系统并不会抛出越界异常。
- 与获取数组中的元素相同,通过
<!-- EL可以通过索引访问List,但无法访问Set。因为Set中没有索引的概念-->
<%
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
names.add("王五");
pageContext.setAttribute( "names", names);
%>
names[2] = ${names[2] }<br>
- (6): 获取
Map
中的元素EL
通过${attributeName.mapKey}
的方式可以获取指定Map
的指定key
的值。
<%
Map<String,0bject> map = new HashMap<>();
map.put( "school", new School("清华大学","北京海淀"));
map.put( "mobile","1234567");
map.put( "age",21);
pagecontext.setAttribute( "map",map);
%>
schoo1.name = ${map.school }<br>
schoo1.name = ${map.school.sname }<br>
mobile = ${map.mobile } <br>
age = ${map.age } <br>
2.4.2 运算符
- EL表达式可以进行各种运算,其中常用的运算符有:
序号 | 种类 | 运算符 |
---|---|---|
1 | 算术运算符 | + ,- , * , / ,% ,(不支持++ , -- ) |
2 | 关系运算符 | == , != , > ,>= , < , <= |
3 | 逻辑运算符 | ! , && , not ,and ,or |
4 | 条件运算符 | ? , : |
5 | 取值运算符 | [] , 点号. |
- 除了上述运算符外还有一个非常有用的运算符
empty
,其用法为${empty 变量}
,结果为布尔值。- 若变量未定义,则返回值为
true
- 若变量为
string
类型,且其值为空串,则返回值为true
。 - 若变量为引用类型,且其值为
null
,则返回值为true
。 - 若变量为集合类型,且其不包含任何元素,则返回值为
true
。
- 若变量未定义,则返回值为
<%
String username = null;
pageContext.setAttribute( "username", username);
String schoolName = "";
pageContext.setAttribute("schoolName","schoolName")
List<Student> students = new ArrayList<>();
pageContext.setAttribute("students","students")
%>
<!-- empty对于没有定义的变量的运算结果为: true -->
empty name = ${empty name } <br>
<!-- empty对于为null的引用的运算结果为: true -->
empty username = ${empty username } <br>
<!-- empty对于为空串的String引用的运算结果为: true -->
empty schoolName = ${empty schoolName } <br>
<!-- empty对于没有元素的数组或集合的运算结果为: true -->
empty students = ${empty students } <br>
2.4.3 内置对象
概念: 就像JSP
的Java
代码块及表达式块中可以使用九个内置对象一样,EL
表达式中,同样也存在有内置对象,并且存在11
个内置对象。常用的内置对象,除了前面使用过的四个域属性空间相关的内置对象外,还有如下几个。
- (1) pageContext : 该
pageContext
与JSP
内置对象中的pageContext
是同一个对象。通过该对象,可以获取到request
、response
、session
、servletContext
、servletConfig
等对象。注意,这些对象在EL
中不是内置对象。这些对象只能通过pageContext
获取。- 在
EL
中直接${pageContext.request}
即可获取request
对象。当然,其底层实际调用的是pageContext.getRequest()
方法。同理,也可以通过类似方式获取到其它对象。 - 在这些获取的对象中,有一个是实际工程中最常用的:
${pageContext.request.contextPath}
,用于获取当前项目的发布到服务器的名称。一般会用在JSP
页面的路径前。
- 在
<form action="${pageContext.request.contextPath }/register.do" method="POST">
<!-- 。. . -->
</form>
在
EL
的11个内置对象中除了pageContext
外,其它10个内置对象,其类型均为java.util.Map
类型。
- web:
index.jsp
<form action="${pageContext.request.contextPath }/show.jsp"
method="POST">
姓名: <input type="text" name="name" /><br>
年龄:<input type="text"name="age" /><br>
爱好:
<input type="checkbox" name="hobby" value="swimming"/>游泳
<input type="checkbox" name="hobby" value="climbing" />登山
<input type="checkbox" name="hobby" value="reading"/>读书
<br>
<input type="submit" value="注册" />
</form>
-
web:
show.jsp
-
(2) param :
- 在
EL
中通过${param.参数名}
可获取到请求中指定参数名的值。例如,提交的请求为:127.0.0.1:8080/01-jsp-primary/index.jsp?name=abc
- 在
JSP
页面中通过如下方式,可获取到name
参数的值为abc
。param.name = ${param.name } <br>
- 在
<!--获取请求中的指定参数的所有值,其底层实际调用的是:
request.getParameterValues()
-->
hobby[0] = ${paramValues.hobby[0] } <br>
hobby[1] = $iparamValues.hobby[1] }<br>
hobby[2] = $iparamValues.hobby[2] } <br>
- (3) paramValues :
- 若提交的请求中同一参数具有多个值,则可通过
${paramvalues.参数名[索引]}
获取到指定索引号的该参数值。例如,提交的请求为:127.0.0.1:8080/01-jsp-primary/index.jspthobby=swimming&hobby=reading
- 若提交的请求中同一参数具有多个值,则可通过
<!--获取请求中的指定参数的所有值,其底层实际调用的是:
request.getParameterValues()
-->
hobby[0] = ${paramValues.hobby[0] } <br>
hobby[1] = $iparamValues.hobby[1] }<br>
hobby[2] = $iparamValues.hobby[2] } <br>
- (4) initParam :
- 表示获取
web.xml
中定义的参数encoding
的值。
- 表示获取
<!-- web.xml 配置
<context-param>
<param-name>company</param-name>
<param-value>bjpowernode</param-value>
</context-param>
<context-param>
<param-name>address</param-name>
<param-value>中国北京</param-value>
</context-param>
-->
<!--获取初始化参数 其底层调用的是
servletContext.getInitParameter();
-->
company = ${initParam.company } <br>
address = ${initParam.address } <br>
2.4.4 JSTL中的EL函数
-
(1) JSTL :
Apache
已经定义好了一套标准的标签库规范,称为JSTL
,JSP Standard Tag Library
,即JSP
标准标签库。该规范已通过JCP
审核认定。- 在
JSTL
中,已经定义好了一套对于字符串进行处理的函数标签库,这个函数标签库中定义了16个对于字符串进行处理的函数。我们在JSP
页面中可以直接使用。 - 当然,需要使用
JSTL
,首先需要将其Jar
包导入。JSTL.jar
+stardand.jar
-
(2) JSTL 的EL函数标签库:
JSTL
函数标签库的.tld
文件存放于standard
的jar
包的META-INF
目录中,文件名为fn.tld
。- 打开
fn.tld
文件,可以看到其<short-name/>
为fn
,即将来JSP
中使得的prefix
前辍为fn
, <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
:其中定义了16个对于字符串进行处理的函数。
- 打开
-
(3) 16个EL函数 :
| 序号 | 函数名 | 功能说明 | | --- | --- | --- | | 1 | fn:contains(String,String) | 判断在源字符串中是否包含目标字符串 | | 2 | fn:containslgnoreCase(String,String) | 判断在源字符串中是否包含目标字符串,并且在判断时忽略大小写 | | 3 | fn:endsWith(String,String) | 判断源字符串是否以指定的目标字符串结尾 | | 4 | fn:startsWith(String,String) | 判断源字符串是否以指定的目标字符串开头 | | 5 | fn:indexOf(String,String) | 在源字符串中查找目标字符串,并返回源字符串中最先与目标字符串匹配的第一个字符的索引 | | 6 | fn:replace(String,String,String) | 把源字符患中的一部分替换为另外的字符串,并返回替换后的字符串 | | 7 | fn:substring(String,int,int) | 获取源字符串中的从第二个参数指定下标开始,到第三个参数减1后的下标结束的子串 | | 8 | fn:substringBefore(String,String) | 获取源字符串中指定子字符串之前的子字符串 | | 9 | fn:substringAfter(String,String) | 获取源字符串中指定子字符串之前的子字符串 | | 10 | fn:split(String,String) | 将源字符串拆分为一个字符串数组 | | 11 | fn:trim(String) | 去除字符串前后的空格 | | 12 | fn:toUpperCase(product.name) | 转为大写字符 | | 13 | fn:toLowerCase(product.name) | 转为小写 | | 14 | fn:escapeXml(param:info) | 把一些字符转成XML表示,例如<字符应该转为< | | 15 | fn:join(array,String) | 将数组中的数据联合成一个新字符串,并使用指定字符格开 | | 16 | fn:length(shoppingCart.products) | 获取字符串的长度,或者数组的大小 |
2.4.5 EL函数总结
EL
不能出现在Java
代码块、表达式块等JSP
的动态代码部分。EL
只能从pageConext
、request
、session
、application
四大域属性空间中获取数据。EL
不会抛出空指针异常。若访回一个null
对象的属性,则什么也不显示。EL
不会抛出数组访问越界异常。若访问一个数组中的不存在的元素,则什么也不显示。EL
不具有对字符串进行处理的能力,就连简单的字符串拼接都不行。
2.5 JSTL
概念: 前面我们自定义的标签库,已经由JCP
的成员Apache
定义好了,并且也已经打包。我们只需要导入该Jar
包后,在页面中使用taglib
指令将其导入即可使用。这套标签库称为JSTL
,JSP Standard Tag Library
,即JSP
标准标签库。JSTL
中定义了五个标签库:
- 核心标签库: 主要用于完成基本的逻辑运算。重点。
- 格式化标签库: 主要用于完成日期、数字的格式化显示。
EL
承数标签库: 定义了若干EL
函数。SQL
操作标签库: 完成SQL
操作。不使用了。对于SQL
操作,已经完全由Java
代码完成。XML
操作标签库: 完成XML
操作。不使用了。对于XML
操作,已经完全由Java
代码完成。
2.5.1 核心标签库
概念: 使用JSTL
的核心标签库,需要在页面中通过taglib
指令首先将标签库导入。
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
- (1) c:set
- (2) c:remove
- (3) c:out
- (4) c:catch
- (5) c:if
- (6) c:choose
- (7) c:forEach