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