JSP (2) 核心操作

174 阅读11分钟

JSP核心

  • jsp:demo1.jsp
<%
session.setAttribute( "user","bjpowernode" );
%>
  • jsp:demo2.jsp
<%
String user = (String)session.getAttribute( "user" );
%>
user = <%=user %>

2.1 JSP的内置对象

概念:JSPJava代码块、表达式块等中可以直接使用的引用,称为JSP的内置对象。常用的内置对象有九个,分别是:

序号内置对象名(引用名)类型
1pageContextejavax.servlet.jsp.PageContext
2requestjavax.servlet.http.HttpServletRequest
3sessionjavax.servlet.http.HttpSession
4applicationjavax.servlet.ServletContext
5responsejavax.servlet.Http.HttpServletResponse
6configjavax.servlet.ServletConfig
7outjavax.servlet.jsp.JspWrite*r
8pagejava.long.Object
9exceptionjava.long.Throwable
  • 这九个对象在JSPJava代码块、表达式块中可以直接使用。只所以可以直接使用,是因为Java代码块与表达式块被JSP引擎翻译后均出现在了Servlet_jspService()方法中。而这九个对象,就是_jspService()方法中的局部变量。在JSPJava代码块、表达式块中的代码就是_jspService()方法中的代码,所以在其中可以直接使用。

2.1.1 pageContexte

概念:

  • pageContext,页面上下文,其具有一个只在当前页面范围的域属性空间,即其具有setAttribute()方法与getAttribute()方法。但,由于在当前页面范围,数据不存放到域属性空间也可直接使用,将数据存放到域属性空间反而感觉“多此一举”,所以这些方法并不常用。不过,在同一页面中,为了使用EL表达式(后面学习)来访问某变量的值,此时一般使用pageContext
  • pageContext具有一些get方法,可以获取到 Request,ResponseSession,ServletContextservletConfigpage(即当前Servlet)、exceptionout等另外八个内置对象。
    • 但由于这些对象本身就是内置对象,在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) ;
%>

image.png

2.1.4 page

  • 查看JSP翻译为的Servlet,可以看到page对象即servlet对象本身。这个对象在实际应用中并不常用。

2.1.5 exception

  • 在普通的JSP页面中是不能使用exception内置对象的。因为打开JSP翻译为的Servlet,发现其中并没有excebtion对象。若要在页面中直接使用exception对象,则需要配合着page指令使用。page指令后面讲解。

2.1.6 其他对象

  • 其它对象,还有requestresponsesession,及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

image.png

  • (2): 静态联编
  • 查看Tomcatwork目录中相关子目录,发现只生成了一个index_jsp.javaservlet源文件,并没有生成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动作很多,但在实际开发时常用的就两个:转发动作与包含动作。
  • 这两份个动作的完成,底层使用的是RequestDispatcherforward()include()方法实现的。而这两份种请求转拔方式的本质区别是,标准输出流的开启时间不同。forward()方式的标准输出流是在目标资源中开启的标准输出流,而include()方式的标准输出流则是在当前发出包含运作的页面中开启的。所以,forward()动作的发起页面中是无法向标准输出流中写入数据的;而include()动作的发起页面及目标页面中均可向标准输出流中写入数据。
  • 这两份个动作都具有一个page属性,用于指定要转向的页面。

2.3.1 forward动作

  • 页面中一旦具有了forward动作,那么当前页面中的所有要显示的内容都将无法显示。因为页面直接转发到了下一个页面。

image.png

2.3.2 include动作

  • (1): include动作元素可以在JSP页面中动态包含一个文件,文件的内容可以是静态的文件也可以是动态的脚本,而且当包含的动态文件被修改的时候JSP引擎可以动态对其进行编译更新。 语法: <jsp:include></jsp:include>
  • (2): 动态联编
  • 打开Tomcatwork目录的相关子目录,可以看到有两个.java文件:index_jsp.javaleft_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只能从 pageConextrequestsessionapplication四大域属性空间中获取数据。以下方式是无法获取到指定数据的。因为这个数据没有存放在四大域属性空间中。
<%
int sum = 50;
%>

<!--这个数据sum是取不出来的。
因为其没有在四大域属性空间中 -->
sum =${sum }<br>
  • (2): 从指定域中获取数据
    • pageContext依次查找到 application域空间,会降低扰行效率。若某属性确定存放在某个域属性空间,则可指定直接从该空间中查找。此时需要借助EL的四个域属性空间相关的内置对象。
序号内置对象说明
1pageScopepage范围域属性空间中查找指定的key
2resultScoperesult范围域属性空间中查找指定的key
3sessionScopesession范围域属性空间中查找指定的key
4applicationScopeapplication范围域属性空间中查找指定的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["属性"}的方式获取。该方式不常用。
<%
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 内置对象

概念: 就像JSPJava代码块及表达式块中可以使用九个内置对象一样,EL表达式中,同样也存在有内置对象,并且存在11个内置对象。常用的内置对象,除了前面使用过的四个域属性空间相关的内置对象外,还有如下几个。

  • (1) pageContext :pageContextJSP内置对象中的pageContext是同一个对象。通过该对象,可以获取到requestresponsesessionservletContextservletConfig 等对象。注意,这些对象在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文件存放于standardjar包的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只能从pageConextrequestsessionapplication四大域属性空间中获取数据。
  • EL不会抛出空指针异常。若访回一个null对象的属性,则什么也不显示。
  • EL不会抛出数组访问越界异常。若访问一个数组中的不存在的元素,则什么也不显示。
  • EL不具有对字符串进行处理的能力,就连简单的字符串拼接都不行。

2.5 JSTL

概念: 前面我们自定义的标签库,已经由JCP的成员Apache定义好了,并且也已经打包。我们只需要导入该Jar包后,在页面中使用taglib指令将其导入即可使用。这套标签库称为JSTLJSP 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