【Java Web编程 六】深入理解EL表达式和JSTL标签(上)

简介: 【Java Web编程 六】深入理解EL表达式和JSTL标签(上)

在正式学习Servlet之前,我们还是把JSP的全部内容搞定,剩下的EL表达式和JSTL标签其实没什么特别的,还是为了我们一直提到的JSP的目标:简化Java代码的写法,尽量让页面干净整洁

EL表达式

什么是EL表达式呢?官方解释为:表达式语言(Expression Language,EL)是一种简单的数据访问语言,基本的语法格式为${ 表达式 },主要用于在页面上生成动态内容,并代替 JSP 脚本元素。其实说通俗点儿,EL表达式就是JSP内置的表达式:作用是来替代JSP的默认语法:<%=%>

基本功能

EL表达式的标准格式为:${el表达式},有以下几个基本功能,其实可以看的出来分别对应了我们前面三篇Blog中对<%=%>的使用,基本语法,内置对象使用和操作Java Bean:

  • 输出内容&执行计算【基本语法】:使用EL表达式可以方便地向客户端打印值,并且表达式支持一系列的逻辑运算
  • 操作隐式对象【内置对象】:使用一系列的隐含对象,如 pageContext 对象、request 对象和 session对象
  • 操作JavaBean对象【Java Bean】:使用${JavaBean.property} 的形式访问作用域中 JavaBean 对象的属性

接下来我们分别从这三个角度对比下写法。

输出内容&执行计算

从基本语法的角度来看,EL表达式好像并没有减少太多原有的<%=%>写法的数量,只是通过符号使整个页面变得更简单了:

代码清单对比示例

我们来看下输出内容和执行计算的内容:

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="com.example.MyFirstJavaWeb.javabean.Person" %><%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/7/18
  Time: 18:46
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
  String name = "tml"; // 定义姓名
  int age = 80; // 定义年龄
  String sex = "男"; // 定义性别
  pageContext.setAttribute("name", name); // 放入 page 范围内
  pageContext.setAttribute("age", age);
  pageContext.setAttribute("sex", sex);
  List listNull=null;
  pageContext.setAttribute("listNull", listNull);
  List list=new ArrayList();
  list.add("22");
  pageContext.setAttribute("list", list);
%>
<h3>
  旧的写法:姓名:<%=name%>  年龄:<%=age%>  性别:<%=sex%>,域中listNull的长度是否为0:<%=listNull==null?"true":"false"%> , 域中list的长度是否为0:<%=list.size()==0?"true":"false"%>
  </br>
  新的写法:姓名:${name}  年龄:${age}  性别:${sex}, 域中listNull的长度是否为0: ${empty listNull }, 域中list的长度是否为0: ${empty list }
</h3>
<%
  Person person=new Person();
  //将person放入域中
  request.setAttribute("person", person);
%>
获取域中person的name值【EL】: ${person.name } <br>
获取域中person的name值【旧】:<%=((Person)request.getAttribute("person")).getName() %>
<%
  String a = "tml";
  String b = "帅";
  pageContext.setAttribute("a", a);
  pageContext.setAttribute("b", b);
%>
<br>
a+b字符串相连的结果为【EL】:${a}${b} <br/>
a+b字符串相连的结果为【旧】:<%=a+b%>
<%
  int para1 = 15;
  int para2 = 13;
  pageContext.setAttribute("para1", para1);
  pageContext.setAttribute("para2", para2);
%>
<br>
para1和para2比较的结果【EL】:${para1==para2}<br/>
para1和para2比较的结果【旧】:${para1==para2}
<%
  boolean flag = true;
  pageContext.setAttribute("flag", flag);
%>
<br>
para1取反的结果【EL】:${not flag}<br/>
para1取反的结果【旧】:<%=!flag%>
</body>
</html>

返回结果为:

其实从以上内容可以看的出来,简单的输出上EL并没有什么优势,甚至如果不把变量显式放到域里EL都获取不到,但是一些特殊的逻辑判断和运算,EL是有优势的。

EL的运算符

为了方便JSP中实现计算和逻辑判断功能,EL表达式提供了如下几种运算符:存取数据运算符,算术运算符,关系运算符,逻辑运算符,条件运算符,empty 运算符,从以上的例子我们知道,其实这才是基本语法中EL的大杀器,通过定义特定的语法让基本的表达式逻辑写法更简化。

存储数据运算符

在 EL 表达式中可以使用运算符[].来取得对象的属性,如 ${person.name} 或者 ${person["name"]} 表示取出对象 person中的 name 属性值:

<%
  Person person=new Person();
  //将person放入域中
  request.setAttribute("person", person);
%>
获取域中person的name值【EL】: ${person.name } <br>
获取域中person的name值【旧】:<%=((Person)request.getAttribute("person")).getName() %>

算术运算符

算术运算符可以作用于整数和浮点数。EL 表达式中的算术运算符主要有 5 个:

注意:EL表达式无法像Java一样将两个字符串用+运算符相连接,如${"a"+"b"}的写法是错误的,但可以使用${"a"}${"b"}的写法来表示:

<%
  String a = "tml";
  String b = "帅";
  pageContext.setAttribute("a", a);
  pageContext.setAttribute("b", b);
%>
<br>
a+b字符串相连的结果为:${a}${b}

关系运算符

关系运算符作用在整数和浮点数上,可以依据字母的顺序比较两个字符串的大小:

在使用 EL 关系表达式时,不能写成 ${param.password1}==${param.password2},而应写成 ${param.password1==param.password2}

<%
  int para1 = 15;
  int para2 = 13;
  pageContext.setAttribute("para1", para1);
  pageContext.setAttribute("para2", para2);
%>
<br>
para1和para2比较的结果:${para1==para2}

逻辑运算符

逻辑运算符主要用于连接多个关系运算符,实现并且、或者关系判断,EL 表达式中的逻辑运算符主要包含3个

代码示例如下:

<%
  boolean flag = true;
  pageContext.setAttribute("flag", flag);
%>
<br>
para1取反的结果【EL】:${not flag}<br/>
para1取反的结果【旧】:<%=!flag%>

empty 运算符

empty 运算符是一个前缀运算符,用于判断对象或变量是否为 null 或空,语法格式为:${empty 变量或对象 }

  • ${empty A} 指向的A对象为 null 时,表达式 ${empty A}返回 true
  • ${empty A} 中A 是空字符串时,表达式 ${empty A} 返回 true
  • ${empty A}当 A 是集合或数组时,如果 A 中没有任何元素,则表达式 ${empty A}返回 true

代码示例如下:

<%
  String name = "tml"; // 定义姓名
  int age = 80; // 定义年龄
  String sex = "男"; // 定义性别
  pageContext.setAttribute("name", name); // 放入 page 范围内
  pageContext.setAttribute("age", age);
  pageContext.setAttribute("sex", sex);
  List listNull=null;
  pageContext.setAttribute("listNull", listNull);
  List list=new ArrayList();
  list.add("22");
  pageContext.setAttribute("list", list);
%>
<h3>
  旧的写法:姓名:<%=name%>  年龄:<%=age%>  性别:<%=sex%>,域中listNull的长度是否为0:<%=listNull==null?"true":"false"%> , 域中list的长度是否为0:<%=list.size()==0?"true":"false"%>
  </br>
  新的写法:姓名:${name}  年龄:${age}  性别:${sex}, 域中listNull的长度是否为0: ${empty listNull }, 域中list的长度是否为0: ${empty list }

条件运算符

条件运算符的格式为:A?B:C,它表示根据表达式 A 的结果选择执行 B 或 C,类似于Java中的三目判断运算符,首先将表达式 A 的计算结果转换为布尔型,如果表达式 A 的计算结果为 true,则执行 B 并返回表达式 B 的值;如果计算结果为 false,则执行表达式 C 并返回表达式 C 的值。

例如,表达式 ${1==2?"true":"false"} 的运行结果为 false

运算符优先级

EL 中的运算符有不同的运算优先级,而同一行中的运算符,则按照运算符在表达式中的顺序从左到右依次进行计算。所有运算符的优先级别从高到低如下:

[ ] ( )
not ! empty
算术运算符
关系运算符
逻辑运算符
三目运算符

操作隐式对象

为了更方便的读取Cookie、HTTP 请求消息头字段、请求参数以及 Web 应用程序的初始化参数等信息,JSP中定义了如下隐含对象:

需要注意:不要将 EL 表达式中的隐含对象与 JSP 隐含对象混淆,其中只有 pageContext 对象是两者共用的。

代码清单对比示例

整体的代码清单如下:

<%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/7/18
  Time: 22:41
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>Title</title>
</head>
<body>
<table border="1" align="left">
  <tr><td> 对 pageContext 隐含对象的调用【EL】 </td></tr>
  <tr><td>${pageContext.request.requestURL}</td></tr>
  <tr><td>${pageContext.request.contextPath}</td></tr>
  <tr><td>${pageContext.request.method}</td></tr>
</table>
</table>
<br/>
<br/>
<br/>
<br/>
<br/>
<%
  pageContext.setAttribute("userName", " 访问放入 page 范围内的 Java");
  request.setAttribute("userName", " 访问放入 request 范围内的 Java");
  session.setAttribute("userName", " 访问放入 session 范围内的 Java");
  application.setAttribute("userName", " 访问放入 application 范围内的 Java");
%>
<br/> ${pageScope.userName }
<br/> ${requestScope.userName }
<br/> ${sessionScope.userName }
<br/> ${applicationScope.userName }
<br/>
<br/>
旧的写法
<br/>
<br/> <%=pageContext.getAttribute("userName")%>
<br/> <%=request.getAttribute("userName")%>
<br/> <%=session.getAttribute("userName")%>
<br/> <%=application.getAttribute("userName")%>
<br/>
</body>
</html>

返回结果为:

pageContext隐含对象

我们来看下pageContext的一些使用示例,看看pageContext的一些用法:

<table border="1" align="left">
  <tr><td> 对 pageContext 隐含对象的调用 </td></tr>
  <tr><td>${pageContext.request.requestURL}</td></tr>
  <tr><td>${pageContext.request.contextPath}</td></tr>
  <tr><td>${pageContext.request.method}</td></tr>
</table>

作用域隐含对象

不同的作用域可以放置不同的对象,EL也支持直接用隐含对象获取,我们来看下旧的写法和EL写法的对比:

<%
  pageContext.setAttribute("userName", " 访问放入 page 范围内的 Java");
  request.setAttribute("userName", " 访问放入 request 范围内的 Java");
  session.setAttribute("userName", " 访问放入 session 范围内的 Java");
  application.setAttribute("userName", " 访问放入 application 范围内的 Java");
%>
<br/> ${pageScope.userName }
<br/> ${requestScope.userName }
<br/> ${sessionScope.userName }
<br/> ${applicationScope.userName }
<br/>
<br/>
旧的写法
<br/>
<br/> <%=pageContext.getAttribute("userName")%>
<br/> <%=request.getAttribute("userName")%>
<br/> <%=session.getAttribute("userName")%>
<br/> <%=application.getAttribute("userName")%>

这里对比可以看的出来EL表达式写法确实更加简洁。

操作JavaBean对象

之前的Blog我们提到过通过JSP动作标签和脚本代码两种方式来访问JavaBean对象,这次我们用更加简化的方式,使用EL表达式来操作Java Bean对象,这里还使用我们上篇Blog中的Person对象:

<%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/7/18
  Time: 23:06
  To change this template use File | Settings | File Templates.
--%>
<%--导入javabean类--%>
<%@ page import="com.example.MyFirstJavaWeb.javabean.*" pageEncoding="UTF-8" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%-- JSP脚本段--%>
JSP脚本段
<br>
<%
    Person p = new Person();
    out.print(p.getAge() + "<br>");
    out.print(p.getName() + "<br>");
    out.print(p.getSex() + "<br>");
%>
<br>
<br>
<%-- JSP动作标签--%>
JSP动作标签
<%--id表示被实例化的对象名字,class表示类这是javabea声明标签--%>
<jsp:useBean id="person" class="com.example.MyFirstJavaWeb.javabean.Person" scope="request"></jsp:useBean>
<br>
<jsp:getProperty property="name" name="person"/>
<br>
<jsp:getProperty property="age" name="person"/>
<br>
<jsp:getProperty property="sex" name="person"/>
<br>
<br>
<%-- EL表达式--%>
EL表达式
<jsp:useBean id="personEl" class="com.example.MyFirstJavaWeb.javabean.Person" scope="request"></jsp:useBean>
<br>
${personEl.name}
<br>
${personEl.age}
<br>
${personEl.sex}
<br>
</body>
</html>

输出的结果都是相同的,我们可以看的出来脚本段掺杂了换行元素,Jsp动作标识比较冗长,而EL表达式则是最合适的方式:

JSTL标签

什么是JSTL标签,官方的定义为:JSP 标准标签库(JavaServer Pages Standard Tag Library,JSTL),包含用于编写和开发 JSP 页面的一组标准标签,它可以为用户提供一个无脚本环境,通常和EL表达式配合使用

好的,说白了就是发明出来为了取代<% %><%! %>脚本段的,还是那个目标,简化简化再简化,直至JSP页面没有脚本代码。也就是说EL+JSTL就可以取代页面的所有脚本逻辑

相关文章
|
1月前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
24天前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
4天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
8天前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
36 12
|
26天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
48 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
4天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
57 2
|
27天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
27天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
21天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
21天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
44 3