EL & JSTL
在 JSP 中使用原生 java 代码会造成页面可读性很差,难以维护,所以就想出了使用标签来解决这个问题。标签的思想就是,使用的一种看起来更简洁、用起来方便的符号来替代在 JSP 页面中写 java 代码,使得编写 JSP 页面变的更简单。通过查看源码即可证明,事实如此。
EL 表达式
EL 表达式,全称为 Expression Language 。把它理解成是一个在 JSP 页面中通过 .
这个运算符就可以访问 page、request、session、appliaction 四个域对象内部属性的符号就可以了。使用的时候,最好显示指定是从哪个域中获取属性,比如:${requestScope.userName}
。如果不指定,它就会从最小的域 page 中查找,找不到就到去 request 然后是 session 最后是 application 。
表达式的运算顺序是从左到右,将运算结果强制以字符串形式输出。以字符串形式输出、以字符串形式输出、以字符串形式输出,重要的事情说三遍。
通常它都是配合 JSTL 标签来一起使用的,可以实现无 java 代码的 JSP 页面,而且支持多级查找。
EL 表达式的运算符分类
虽然取值是 el 最常用的,但有时候也会需要它来进行运算。 EL 表达式的运算符可以分为以下几类
- 算数运算符
-
+
-
*
/
%
-
- .
- 进行取值操作
- 关系运算符
-
>
<
==
!=
-
- 逻辑运算符
-
&&
||
!
-
- 条件运算符
- ${statement ? a : b} ,这就是三目运算符,此表达式输出为 true 或者 false
- empty 运算符
- ${empty x},判断 x 是否为空,空返回 true ,否则返回 false 。x 为 null 、空串、空集合、空数组都会返回 true !
JSTL
JSTL 全称是 JavaServer Pages Standard Tag Libary ,也就是 JSP 标准标签库。它可以分为 5 类,分别是:核心标签库、国际化标签库、JSTL 函数、SQL 标签、XML 标签。
要想要在 JSP 中使用 JSTL 标签库,必须要使用 JSP 的 taglib 指令将指定的标签库导入到 JSP 文件中,类似于 import 的功能!比如使用<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
表示想要使用核心标签库。也就是说必须要指定 url 和 prefix 两个属性,其他标签库如下表:
类别名称 | 类别英文名称 | uri | prefix |
---|---|---|---|
核心库 | core | http://java.sun.com/jsp/jstl/core |
c |
国际化库 | fmt | http://java.sun.com/jsp/jstl/fmt |
fmt |
函数库 | functions | http://java.sun.com/jsp/jstl/functions |
fn |
sql库 | sql | http://java.sun.com/jsp/jstl/sql |
sql |
xml库 | xml | http://java.sun.com/jsp/jstl/xml |
xml |
知道 JSTL 可以分为这 5 个标签库就可以了,目前只要掌握核心库的部分标签即可,sql 库和 xml 库基本不会用它,因为如今的 JSP 只负责显示。想象自定义标签是怎么做的,就能理解 JSTL 标签的核心原理了,下面有讲到。
JSTL 中的核心库
核心库中设计了一些用来替代 Java 代码中的 if else for循环等语句的功能,而且它的功能更强大,用起来更方便。
核心库中的 if 标签
该标签就是对应着 Java 语言中的 if 语句。if 标签有三个属性,分别是:
-
test
,这是 boolean 类型的一个变量,表示判断的结果是 true 还是 false -
var
,JSTL 标签的底层代码实现是利用了四个域对象做存储的,它运行的中间结果是以属性的方式设置到 page 等域对象中的。所以说这个 var 变量存储的就是属性名,test 变量存储的就是属性值。不需要用到时可省略! -
scope
,用于指定到底应该将 var 和 test 存储到那个域对象中去。不需要用到时可省略!
//使用范例
<c:if test= "${requestScope.userName == `keqi`}" var="isLogin" scope= "sessionScope">
login success.
</c:if>
//如果要用 else 怎么办呢?
//通过 el 表达式从scope中取值进行else的逻辑处理
//如:${sessionScope.isLogin}
上述代码的含义是,判断 request 对象中 userName 属性是否等于 keqi ,如果等于会执行标签体中的内容,也就是会输出 login seccess. 。这个过程相当于以下 Java 代码:
//el表达式取得数据,然后将判断结果赋值到 test 中
userName = request.getAttribute("userName");
boolean test = userName.equals("keqi");
//将 var 和 test 作为属性名和属性值赋值到 session 对象中
session.setAttribute("isLogin",test);
以后在使用此 if 标签时,如果是仅需要用到 test 属性,另外两个就可以省略,否则三个属性必须写全来。此标签的逻辑无非就是利用 el 表达式进行逻辑判断,将判断结果输出到 test 中,然后把它和 var 组成键值对,放到对应的 scope 对象中去而已。
核心库中的 choose 、when 和 otherwise 标签
这三个标签就相当于是 java 语言中的 switch 、case 和 default 语句,下面以一个 request 中属性名为 fun 的属性为例,进行判断。
<c:choose>
<c:when test="${requestScope.fun == 'A'}">
A
</c:when>
<c:when test="${requestScope.fun == 'B'}">
B
</c:when>
<c:otherwise>
C
</c:otherwise>
</c:choose>
上述标签的就相当于是以下 Java 代码。
switch(fun){
case: A
//输出A
break;
case: B
//输出B
break;
default:
//输出C
break;
}
从这里可以得出学习启示,在使用 JSP 标签的时候,结合 Java 中对应的语句去理解,去记忆,去思考就简单了。
核心库中的 foreach 标签
foreach 标签顾名思义就是实现循环功能的。它有两种使用方式,且有 6 个属性分别是:
-
var
每次循环的值都作为此属性的 value -
items
放置数组或者集合对象 -
varStatus
记录当前循环的状态,也就是循环了第几次 -
begin
指定循环从此开始 -
end
指定循环到此结束 -
step
指定每次循环跳跃步数
foreach 标签第一种使用方式,和 Java 中的普通 for 循环相同。
//此标签将循环从1输出到5
<c:foreach var="x" begin="1" end="5" step="1">
${x}
</c:foreach>
foreach 标签第二种使用方式,其中 items 属性要放在 var 前面
//循环输出了list集合对象
<c:foreach items="${list} var="x">
${x}
</c:foreach>
//循环输出map集合对象,直接使用.key 和.value 进行输出
<c:foreach items="${map}" var="x" >
${x.key}:${x.value}
</c:foreach>
国际化标签库中还有对数字和日期进行格式转换的标签,函数库中还有很多针对字符串的标签,以后用到了再来总结。
自定义标签库
JSTL 还支持定义个性化的标签库,总共分为三个步骤:
- 编写标签处理器类
- 在 .tld 文件中注册标签处理器类
- JSP 页面中使用标签
定义一个类继承 SimpleTagSupport 类,重写它的doTag()
和setJspContext()
方法。JSP 中使用标签时,调用的就是这个 doTag() 方法。
public class MyFirstTag extends SimpleTagSupport{
JspContext jspContext;
public void doTag() throws IOException, JspException {
jspContext.getOut().print("This is my first tag.");
}
public void setJspContext(JspContext jspContext) {
this.jspContext = jspContext;
}
}
编写一个标签库文件 .tld ,并注册此标签
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_1.xsd"
version="2.1">
<tlib-version>1.0</tlib-version>
<short-name>My First Taglib Example</short-name>
<!--JSP页面中是通过此url找到该文件的-->
<uri>http://www.keqi.tag</uri>
<tag>
<name>firstTag</name>
<tag-class>app06a.customtag.MyFirstTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
在 JSP 页面中使用自定义的 firstTag 标签
<%@ taglib uri="http://www.keqi.tag" prefix="easy"%>
<html>
<head>
<title>Testing my first tag</title>
</head>
<body>
Hello!!!!
<br/>
<easy:firstTag></easy:firstTag>
</body>
</html>
从这里可以发现每个标签库对应一个 .tld 文件,每个标签对应该文件中的一个tag
元素。标签背后都有一个 Java 类,每次使用标签都是调用这个类的 doTag() 方法。
总结就是,每次使用标签都是这样的流程:通过 url 找到标签库的 .tld 文件,通过标签名找到对应的类,然后调用该类的 doTag() 方法执行该标签完成的功能而已。理解了这一点,就不会觉得标签很虚了,不知道是设么东西了。
补充:这里的 .tld 文件其实就是一个 xml 文件,但这时候会有一个疑问。明明是 xml 文件,为什么后缀又是 .tld 呢?其实对于计算机来说,根本就没有什么文件格式的区分,都是二进制文件。后缀名只是用来给人看的,表示这是一个什么类型的文件,具体的应用软件会区分文件格式,是因为它也是使用者。一句话,文件的后缀名其实一点也不特殊,以后不要被那种看起来就感觉很牛逼的文件后缀名给吓到了,其实他什么都不是。