EL&JSTL
1. EL表达式
1.1. 传统JSP代码的痛点
<table>
<tr>
<th>ID</th>
<th>姓名</th>
<th>称号</th>
<th>头像</th>
</tr>
<%
Object obj = request.getAttribute("list");
List<UserInfo> list = (List<UserInfo>) obj;
for (UserInfo u : list) {
%>
<tr>
<td><%=u.getId()%></td>
<td><%=u.getName()%></td>
<td><%=u.getTitle()%></td>
<td><img src="<%=u.getImg()%>" alt=""></td>
</tr>
<% } %>
</table>
以上jsp页面中既有html代码,也有Java代码,并且Java代码还必须包含在<%%>
之间,可读性行非常差,并且代码编写起来相对复杂:
- 可读性差
- 编写复杂
1.2. EL表达式概述
EL(Expression Language)表达式语言,是JSP2.0规范中引入一种新特性,灵感来自于ECMAScript中XPath(快速节点检索技术)语法;允许使用${}
操作jsp各个作用域中的数据,另外EL表达式还引入了一些内置对象,也支持一些基本的运算操作。
1.3. EL基础语法
EL表达式基本语法为:
${表达式内容}
例如:
<!-- 基本运算 -->
${4 + 5}
<!-- 从session返回获取数据 -->
${sessionScope.msg} 等同于 <%=session.getAttribute("msg")%>
<!-- 获取cookie数据 -->
${cookie.name}
注意事项:
由于目前最新版本中为了防止表达式的冲突,jsp默认关闭了对EL表达式的识别,因此如果需要在jsp页面中使用EL表达式,则必须开启EL表达式的支持:
<%@ page language="java" pageEncoding="UTF-8" isELIgnored="false" %>
以上将
isElIgnored
设置为false
代表不忽略EL表达式
1.4. 运算符
EL表达式对于基本算数运算和关系运算都有提供相应支持,常见的运算符主要包含如下
分类 | 运算符 | 案例 |
---|---|---|
算术运算符 | +、-、*、/或div、%或mod |
2
+
3
<
b
r
>
{2+3}<br>
2+3<br>{3-2} 3 ∗ 4 < b r > {3*4}<br> 3∗4<br>{6/3}或 6 d i v 3 < b r > {6 div3 }<br> 6div3<br>{10 % 3}或${10 mod 3} |
关系运算符 | ==或eq、!=或ne、<或lt、>或gt、<=或le、>=或ge |
5
=
=
5
或
{ 5 == 5}或
5==5或{5 eq 5} 2 ! = 3 或 {2 != 3}或 2!=3或{2 ne 3} 5 < 6 或 {5 < 6}或 5<6或{5 lt 6} 7 > 6 或 { 7 > 6}或 7>6或{7 gt 6} 8 < = 9 或 {8 <= 9}或 8<=9或{8 le 9} 8 > = 7 或 {8 >= 7}或 8>=7或{8 ge 7} |
布尔逻辑运算符 | &&或and、||或or、!或not | KaTeX parse error: Expected '}', got '&' at position 7: {true &̲& true}或{true and true} t r u e ∥ ∥ f a l s e 或 {true \|\| false}或 true∥∥false或{true or false} ! t r u e 或 {!true}或 !true或{not true} |
其他 | Empty运算符、三目运算符、()运算符 |
e
m
p
t
y
[
′
a
′
,
′
b
′
,
′
c
′
]
<
b
r
>
{empty ['a','b','c']}<br>
empty[′a′,′b′,′c′]<br>{a > b ? a : b} ${10 > 6 ? 10 : 10 > 11 ? 10 : 11} |
1.5. 内置对象(隐含对象)
EL表达式中对于一些JSP/Servlet对象的访问,提供了内置的对象(无需声明直接使用的对象),主要包含如下:
- 作用域对象
- 请求参数对象
- 请求头对象
- 全局初始化参数
- cookie对象
1.5.1. 作用域对象
作用域对象即用于从jsp的四个内置对象中获取数据的对象,分为:
pageScope
requestScope
sessionScope
applicationScope
用法如下:
<p>${pageScope.page}</p>
<p>${requestScope.request}</p>
<p>${sessionScope.session}</p>
<p>${applicationScope.app}</p>
以上代码等同于:
<p><%=pageContext.getAttribute("page")%></p>
<p><%=request.getAttribute("request")%></p>
<p><%=session.getAttribute("session")%></p>
<p><%=application.getAttribute("app")%></p>
实际使用过程中四个隐含对象可以无需编写,例如:
${msg}
以上
${msg}
表示从page范围获取数据,若获取不到则依次向更大的范围搜索:
pageContext < requestScope < sessionScope < applicationScope
1.5.2. 请求参数对象
在servlet/jsp中需要获取客户端提交的数据时一般会使用request.getParamater("XX")
来获取表单或者url参数数据,在EL表达式中对于请求参数的获取也提供了两个隐含对象:
param
(等同于request.getParamater("XX")
)paramValues
(等同于request.getParamaterValues(“XXX”)
)
使用方式:
-
请求地址:
http://localhost/doLogin.jsp?username=softeem&password=123&lang=java&lang=c&lang=python
-
数据获取
Username:${param.username}<br> Password:${param.password}<br> langs:${paramValues.lang}
以上代码等同于:
String username = request.getParameter("username"); String password = request.getParameter("password"); String[] langs = request.getParameterValues("lang");
1.5.3. 其他对象
-
请求头对象
header
:获取请求头对象headerValues
:获取请求头对象,其中所有的值为一个数组对象
请求头:${header}<br> 请求头中指定的属性:${header['host']}--${header.get("host")}<br> ${header['user-agent']}<br> ${headerValues}
-
cookie
:获取cookie对象cookie:${cookie.JSESSIONID.value}<br> 获取cookie中的用户名:${cookie.name.value}
注意事项,使用cookie的name获取的是一个Cookie对象(而非值),如果需要获取cookie的值,则需要在调用
value
属性才行 -
initParam
:全局初始化参数对象全局的初始化的对象主要用于获取
web.xml
文件中context-param
参数值:<!-- 全局初始化参数--> <context-param> <param-name>applicationContextConfig</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
jsp页面中获取方式包含如下:
全局初始化参数获取:${initParam.applicationContextConfig} <hr> <%=application.getInitParameter("applicationContextConfig")%> <hr> <%=request.getServletContext().getInitParameter("applicationContextConfig")%>
以上每一个表达式获取的结果都是一致的:
classpath:applicationContext.xml
-
pageContext
可以通过该对象配置页面的根地址
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <base href="${pageContext.request.contextPath}/"> <title>Title</title> </head> <body> </body> </html>
${pageContext.request.contextPath}
:获取当前项目所在的根地址
1.6. Web项目中WEB-INF
目录问题
实际开发中通常会将jsp
页面都存储在WEB-INF
目录中,WEB-INF
下的资源外界通过重定向的方式访问,只能由服务器请求转发传输到客户端浏览器显示,对jsp的安全访问提供了保障
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aOn2nQaD-1634725310826)(assets/image-20211004114556921.png)]
以上为一个正常的jsp页面在web项目中的存储方式,外界如果发起以下请求:
http://localhost/WEB-INF/pages/page1.jsp
页面会如下显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8zaldhkx-1634725310828)(assets/image-20211004114658706.png)]
所以,对于WEB-INF
下的资源使用重定向的方式是无法访问的,必须由服务端请求转发,因此可以使用如下的Servlet:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktsmfw1b-1634725310829)(assets/image-20211004115145362.png)]
此时请求地址栏只需要按照如下方式请求即可:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-euDHVDS9-1634725310831)(assets/image-20211004115220345.png)]
2. JSTL(标准标签库)
2.1. JSTL概述
EL表达式可以使用简洁的语法实现数据的获取,但是对于一些负责的逻辑判断,或者循环操作,EL可能无法满足需求,因此需要配合一些其他技术共同实现需求,这一项技术称之为JSTL.
JSTL(JSP Standard TagLib)
:jsp标准标签库,标签库即使用一些预定义(自定义)好的标签,替代原本jsp中的<% %>
之类的原始java代码,使得jsp页面中的代码编写风格更为统一。
2.1.1. 传统JSP代码
<table>
<tr>
<th>工号</th>
<th>姓名</th>
<th>职位</th>
<th>生日</th>
<th>月薪</th>
<th>部门</th>
<th>操作</th>
</tr>
<%
Object obj = request.getAttribute("list");
if(obj != null){
List<Emp> list = (List<Emp>) obj;
for(Emp e:list){
%>
<tr>
<td><%=e.getEno()%></td>
<td><%=e.getEname()%></td>
<td><%=e.getJob().equals("2") ? "部门经理" : "普通员工" %></td>
<td>
<%
DateFormat fmt = DateFormat.getDateInstance(); //yyyy-mm-dd
String birth = fmt.format(e.getBirth());
out.write(birth);
%>
</td>
<td>
<%
NumberFormat numberFormat = NumberFormat.getNumberInstance();
String sal = numberFormat.format(e.getSal());
out.write(sal);
%>
</td>
<td><%=e.getDept().getDname()%></td>
<td>
<a href="">详情</a>
<a href="">操作</a>
</td>
</tr>
<%
}
}else{
out.write("<tr><th colspan='7' style='color:#f00'>暂无数据!</th></tr>");
}
%>
</table>
从以上的源代码语法来看,编写方式极其复杂,可读性差。使用JSTL之后以上代码可以统一一种风格:
2.1.2. 使用JSTL的JSP页面
<table>
<tr>
<th>工号</th>
<th>姓名</th>
<th>职位</th>
<th>生日</th>
<th>月薪</th>
<th>部门</th>
<th>操作</th>
</tr>
<c:if test="${empty list || list.size() < 1}">
<tr><th style="color: #ff0000;">暂无数据!</th></tr>
</c:if>
<c:if test="${not empty list}">
<c:forEach items="${list}" var="e">
<tr>
<td>${e.eno}</td>
<td>${e.ename}</td>
<td>${e.job eq "2" ? "部门经理":"普通员工"}</td>
<td><fmt:formatDate value="${e.birth}" pattern="yyyy-MM-dd"/></td>
<td><fmt:formatNumber value="${e.sal}" pattern="###.##"/></td>
<td>${e.dept.dname}</td>
<td>
<a href="">详情</a>
<a href="">操作</a>
</td>
</tr>
</c:forEach>
</c:if>
</table>
2.2. JSTL使用方式
2.2.1. JSTL基本使用
-
在jsp中使用JSTL通常需要引入两个依赖:
jstl.jar
和standard.jar
<!-- 引入jstl标准标签库--> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
-
在需要使用JSTL的页面中导入标签库:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <h1>page1页面</h1> </body> </html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kXRlr77-1634725310833)(assets/image-20211004142745585.png)]
-
使用标签库
<c:out value="${msg}"></c:out>
以上表示一个输出标签,直接在页面中输出
${msg}
所获取的数据
2.3. JSTL常用标签
JSTL标签库主要包含以下标签分类:
- 核心标签库(
c标签
) - 格式化标签库(
fmt标签
) - 函数标签库(
functions标签
) - 数据库标签(
sql标签
) - xml标签
标签库 | 标签库的URI | 前缀 |
---|---|---|
Core | http://java.sun.com/jsp/jstl/core | c |
Format | http://java.sun.com/jsp/jstl/fmt | fmt |
XML | http://java.sun.com/jsp/jstl/xml | xml |
SQL | http://java.sun.com/jsp/jstl/sql | sql |
Functions | http://java.sun.com/jsp/jstl/functions | fn |
2.3.1. c标签
c标签
也称之为核心标签库(core),主要用于页面进行数据的展示,包括一些逻辑判断和循环处理,常用c标签主要包含以下:
-
c:out
:输出标签value
:用于输出的内容(配合el表达式使用,获取四个作用域的数据)default
:如果value无法获取数据时,可以显示该默认值escapeXml
:是否忽略xml代码(包括html),默认值:true
<c:out value="${code}" default="无数据" escapeXml="false"></c:out>
-
c:import
:包含标签,将一个页面包含到当前的页面中(动态包含) -
c:set
:设置参数的标签,用于向指定的范围或者bean中设置属性var
:存储的变量名value
:存储的变量值scope
:存储范围(page,request,session,application)
-
c:url
和c:redirect
-
c:url
:用于创建url的标签var
:该url的属性名value
:url的地址值scope
:url的存储范围(page,request,session,application)
<c:url var="page3" value="/user/page3" scope="request"> <c:param name="username" value="softeem"/> <c:param name="password" value="123456"/> </c:url>
以上代码等同于
request.setAttribute("page3","/user/page3?username=softeem&password=123456")
-
c:redirect
:重定向到指定的目标url
:目标地址,可以使用el表达式获取指定范围的数据
<c:redirect url="${page3}"/>
以上代码等同于
response.sendRedirect("/user/page3?username=softeem&password=123456");
-
2.3.1.1. c:if
标签
<c:if>
标签用于执行一些简单的逻辑判断,包含一个核心属性test
,该属性中可以使用EL编写一个返回布尔结果的表达式:
<c:if test="${age lt 18}">
未成年
</c:if>
<c:if test="${age ge 18 and age le 50}">
中年
</c:if>
<c:if test="${age gt 50}">
老年
</c:if>
c:if
标签并未像java一样对应有一个c:else
2.3.1.2. c:choose
&c:when
&c:overwise
标签
由于c:if
标签只提供一个分支,进行逻辑判断;如果对于存在多个分支的需求可以使用c:choose
和c:when
解决,语法如下:
<c:choose>
<c:when test="${age < 18}">未成年</c:when>
<c:when test="${age >= 18 and age < 36}">青年</c:when>
<c:when test="${age >= 36 and age < 55}">中年</c:when>
<c:when test="${age >= 55 and age < 65}">中老年</c:when>
<c:otherwise>
老年
</c:otherwise>
</c:choose>
2.3.1.3. c:forEach
&c:forTokens
标签
-
c:forEach
在对集合数据遍历时,jsp中除了可以直接使用java代码之外,另外也可以使用jstl中
c:forEach
(推荐),例如:<div class="layui-container"> <table class="layui-table" lay-even lay-skin="line" lay-size="lg"> <tr> <th><input type="checkbox"></th> <th>ID</th> <th>姓名</th> <th>性别</th> <th>年龄</th> <th>电话</th> <th>操作</th> </tr> <c:forEach var="u" items="${requestScope.list}"> <tr> <td><input type="checkbox"></td> <td>${u.id}</td> <td>${u.name}</td> <td>${u.sex}</td> <td>${u.age}</td> <td>${u.tel}</td> <td> <button class="layui-btn layui-btn-normal">详情</button> <button class="layui-btn layui-btn-warm">编辑</button> <button class="layui-btn layui-btn-danger">删除</button> </td> </tr> </c:forEach> </table> </div>
c:forEach
标签中常用的属性-
var
:迭代到的每一个元素的变量名(自定义) -
items
:需要被迭代(遍历)的集合或者数组 -
varStatus
:表示当前迭代的数据状态,内部包含一些常用子属性index
:获取当前数据行的索引,从0开始count
:获取当前行的总行数,即:第几行last
:是否是集合中的最后一行数据first
:是否是集合中的第一行数据
<table class="layui-table"> <tr> <th>索引</th> <th>序号</th> <th>名称</th> <th>是否第一个</th> <th>是否最后一个</th> </tr> <c:forEach var="name" items="admin,jack,rose,tom,jerry" varStatus="stat"> <tr> <td>${stat.index}</td> <td>${stat.count}</td> <td>${name}</td> <td>${stat.first}</td> <td>${stat.last}</td> </tr> </c:forEach> </table>
-
begin
:起始的显示索引 -
end
:结束的显示索引 -
step
:步进值,即每次跳过的行数
<form class="layui-form" style="padding: 20px"> <div class="layui-col-md2"> <select> <c:forEach var="year" begin="2000" end="2021" step="4"> <option>${year}</option> </c:forEach> </select> </div> </form>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xt0754eH-1634725310834)(assets/image-20211004162809436.png)]
-
-
c:forTokens
<c:forTokens>
是按照指定的分隔符对字符串文本内容截取之后获得为集合,并且可以实现对该集合遍历,使用方式:
<% pageContext.setAttribute("tags","乡村/摇滚/R&B/美式");%> <c:forTokens items="${tags}" delims="/" var="tag"> <span class="layui-badge layui-bg-danger">${tag}</span> </c:forTokens>
常用属性:
items
:需要进行截取的字符串delims
:分隔符var
:分割之后的每一个元素的名称
2.3.2. fmt标签
c
标签作为核心标签库是经常需要被使用到的,但是有些时候c标签无法满足所有需求。例如需要对显示的时间日期,或者数值进行格式化的时候,c
标签不能很好的解决,因此需要其他标签辅助解决问题,其中用于进行格式化处理的需求,JSTL提供了fmt
标签
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Y6t3UQR-1634725310836)(assets/image-20211004170812704.png)]
fmt
作用于对日期,数值等数据进行格式化或者解析操作
-
使用
fmt
标签必须先导入标签库<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
-
具体使用
<td> <fmt:formatDate value="${u.regTime}" pattern="yyyy-MM-dd HH:mm:ss"/> </td> <td> <fmt:formatNumber value="${u.money}" pattern="¥###.#"/> </td>
2.3.3. 综合案例
使用JSTL显示如下一个表格效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jEimfpVY-1634725310837)(assets/image-20211004171432856.png)]
-
Servlet代码
@WebServlet("/jstl/*") public class JSTLServlet extends BaseServlet{ public String getData(HttpServletRequest request, HttpServletResponse response){ List<User> list = new ArrayList<User>(){ { add(new User(1,"softeem","0",18,"123456","游戏,小说,帅哥",new Date(),4567.89)); add(new User(2,"admin","0",28,"123456","唱,跳,rap,篮球",new Date(120,8,15),4567.89)); add(new User(3,"刘德华","1",45,"123456","编程,帅哥",new Date(),46.89)); add(new User(4,"张三丰","1",64,null,"游戏,小说,帅哥",new Date(121,6,30),46.19)); add(new User(5,"张无忌","1",88,"123456","游戏,小说,帅哥",new Date(),4567.89)); add(new User(6,"周杰伦","0",27,"123456","看帅哥,听音乐",new Date(119,5,13),1456.42)); add(new User(7,"刘亦菲","1",19,null,"游戏,帅哥",new Date(),561.91)); add(new User(8,"马德华","0",15,"123456","游戏,小说",new Date(),467.18)); } }; request.setAttribute("list",list); return "/WEB-INF/pages/page1.jsp"; } }
-
jsp页面代码
<div class="layui-fluid"> <table class="layui-table" lay-even lay-skin="line" lay-size="lg"> <tr> <th><input type="checkbox"></th> <th>ID</th> <th>姓名</th> <th>性别</th> <th>年龄</th> <th>年龄状态</th> <th>电话</th> <th>兴趣爱好</th> <th>注册时间</th> <td>余额</td> <th>操作</th> </tr> <c:forEach var="u" items="${requestScope.list}"> <tr> <td><input type="checkbox"></td> <td>${u.id}</td> <td>${u.name}</td> <td>${u.sex eq '0' ? '女' : '男'}</td> <td>${u.age}</td> <td> <c:choose> <c:when test="${u.age < 18}">未成年</c:when> <c:when test="${u.age >= 18 and u.age < 36}">青年</c:when> <c:when test="${u.age >= 36 and u.age < 55}">中年</c:when> <c:when test="${u.age >= 55 and u.age < 65}">中老年</c:when> <c:otherwise>老年</c:otherwise> </c:choose> </td> <td> <c:if test="${u.tel eq null}">未知</c:if> <c:if test="${u.tel ne null}">${u.tel}</c:if> </td> <td> <c:forTokens var="h" items="${u.hobbies}" delims=","> <a href=""><span class="layui-badge layui-bg-blue">${h}</span></a> </c:forTokens> </td> <td> <fmt:formatDate value="${u.regTime}" pattern="yyyy-MM-dd HH:mm:ss"/> </td> <td> <fmt:formatNumber value="${u.money}" pattern="¥###.#"/> </td> <td> <button class="layui-btn layui-btn-normal">详情</button> <button class="layui-btn layui-btn-warm">编辑</button> <button class="layui-btn layui-btn-danger">删除</button> </td> </tr> </c:forEach> </table> </div>