【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并发编程:理解并应用ReentrantLock
【4月更文挑战第30天】 在多线程的世界中,高效且安全地管理共享资源是至关重要的。本文深入探讨了Java中的一种强大同步工具——ReentrantLock。我们将从其设计原理出发,通过实例演示其在解决并发问题中的实际应用,以及如何比传统的synchronized关键字提供更灵活的锁定机制。文章还将讨论在使用ReentrantLock时可能遇到的一些挑战和最佳实践,帮助开发者避免常见陷阱,提高程序性能和稳定性。
|
1天前
|
缓存 Java 调度
Java并发编程:深入理解线程池
【4月更文挑战第30天】 在Java并发编程中,线程池是一种重要的工具,它可以帮助我们有效地管理线程,提高系统性能。本文将深入探讨Java线程池的工作原理,如何使用它,以及如何根据实际需求选择合适的线程池策略。
|
1天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】 本文将深入探讨Java中的线程池,解析其原理、使用场景以及如何合理地利用线程池提高程序性能。我们将从线程池的基本概念出发,介绍其内部工作机制,然后通过实例演示如何创建和使用线程池。最后,我们将讨论线程池的优缺点以及在实际应用中需要注意的问题。
|
1天前
|
Java
java lambda 表达式中的双冒号和箭头的用法
java lambda 表达式中的双冒号和箭头的用法
|
1天前
|
Java 大数据 数据库连接
java编程的优点
【4月更文挑战第30天】java编程的优点
4 0
|
1天前
|
存储 安全 Java
【亮剑】Java并发编程中的四个关键字:ThreadLocal、Volatile、Synchronized和Atomic
【4月更文挑战第30天】Java并发编程涉及`ThreadLocal`、`Volatile`、`Synchronized`和`Atomic`四个关键机制。`ThreadLocal`为每个线程提供独立变量副本;`Volatile`确保变量可见性,但不保证原子性;`Synchronized`实现同步锁,保证单线程执行;`Atomic`类利用CAS实现无锁并发控制。理解其原理有助于编写高效线程安全代码。根据业务场景选择合适机制至关重要。
|
1天前
|
安全 Java API
Java 8新特性概述及其对编程实践的影响
【4月更文挑战第30天】本文将详细讨论Java 8的新特性,包括Lambda表达式、Stream API以及Optional类等,并探讨这些新特性如何改变了Java编程的实践。我们将通过实例代码展示这些新特性的用法,并分析其对提高代码可读性和编写效率的影响。
|
1天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将从线程池的基本概念入手,了解其工作原理和优势,然后详细介绍如何使用Java的Executor框架创建和管理线程池。最后,我们将讨论一些高级主题,如自定义线程工厂和拒绝策略。通过本文的学习,你将能够更好地理解和使用Java的线程池,提高你的并发编程能力。
|
1天前
|
存储 安全 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第30天】在Java开发中,并发编程是一个复杂而又关键的领域。它允许多个线程同时执行,从而提高程序性能和资源利用率。然而,并发编程也带来了许多挑战,如数据不一致、死锁和线程安全问题。本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化策略。我们将通过实例分析如何在保证线程安全的同时提高程序性能,为Java开发者提供实用的指导。
|
1天前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。