EL和JSTL

219 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

EL

EL 的全称是表达式语言(Expression Language ),是封装了业务逻辑的页面标签,可以用来替代 JSP 页面中的 Java 代码,使不懂 Java 的开发人员也能写出 JSP 代码。EL 还可以实现自动类型转换等功能,使用起来非常简单。

EL 表达式语法

EL 表达式通常由两部分组成:对象和属性。可以使用 “点操作符”“中括号[]操作符” 来操作对象的属性,其语法格式如下:

${EL表达式}

1. 创建文件

2. 修改pom.xml

修改项目中的 pom.xml 文件如下。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  
  
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.shiyan</groupId>
  <artifactId>ServletProject</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>ServletProject Maven Webapp</name>
  <url>http://maven.apache.org</url>
    <dependencies>
    <!-- servlet 依赖 jar 包 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- jsp 依赖 jar 包 -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
  </dependencies>
    <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <configuration>
                        <!-- 端口 -->
                        <port>8080</port>
                        <!-- 编码-->
                        <uriEncoding>UTF-8</uriEncoding>
                         <!-- 项目的启动路径-->
                        <path>/</path>
                         <!-- 项目名称 -->
                        <finalName>ServletProject </finalName>
                        <!-- 启动的命令名称-->
                        <server>tomcat7</server>
                    </configuration>
                </plugin>
            </plugins>
    </build>
</project>

3. 在 entity 包下创建两个封装数据的 JavaBean。(这里是student和address)

4. 新建 InitServlet.java用于初始化一些数据,并将此 Servlet 配置进 web.xml。

web.xml 的设置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

  <servlet>
    <servlet-name>InitServlet</servlet-name>
    <servlet-class>org.servlet.InitServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>InitServlet</servlet-name>
    <url-pattern>/initServlet</url-pattern>
  </servlet-mapping>
</web-app>

5. 程序会在 InitServlet 中给 student 对象的各个属性赋值,然后跳转到 info.jsp 中。本次先用传统的 Scriptlet 接收对象,并将对象的属性显示到前台如下。

在 src/main/webapp 下新建 info.jsp:

<%@ page pageEncoding="UTF-8"%>
<%@page import="org.entity.*"%>
<html>
    <head>
        <title>info</title>
    </head>
    <body>
        <%
            Student student = (Student)request.getAttribute("student");
            int studentNo = student.getStudentNo();
            String studentName = student.getStudentName();
            Address address = student.getAddress();
            String homeAddress = address.getHomeAddress();
            String schoolAddress = address.getSchoolAddress();

            out.print("学号:"+studentNo +"<br/>");
            out.print("姓名:"+studentName +"<br/>");
            out.print("家庭地址:"+homeAddress +"<br/>");
            out.print("学校地址:"+schoolAddress +"<br/>");
        %>
    </body>
</html>

可以发现,程序的确能够正常显示。但如果将 info.jsp 中的代码用 EL 表达式来实现,就会简单许多。

以下代码是使用 EL 修改后的 info.jsp,功能与之前的 info.jsp 相同。

<%@ page pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<html>
    <head>
        <title>info</title>
    </head>
   <body>
        <%-- 使用 EL 表达式 --%>
        学生对象:${requestScope.student}<br/>
        学号:${requestScope.student.studentNo } <br/>
        姓名:${requestScope.student.studentName } <br/>
        家庭地址:${requestScope.student.address.homeAddress } <br/>
        学校地址:${requestScope.student.address.schoolAddress } <br/>
   </body>
</html>

综上可知使用 EL 可以将 JSP 中的 Java 代码彻底消除,并且不用再做强制的类型转换,整体的 JSP 代码就会简单很多。

EL 表达式操作符示例

1. 点操作符

点操作符 “.” 的用法和在 Java 中的用法相同,都是直接用来调用对象的属性。如 ${requestScope.student},表示在 request 作用域内查找 student 对象。

2. 中括号[]操作符

除了点操作符以外,还可以使用中括号操作符 “[]” 来访问某个对象的属性,例如 ${requestScope.student.studentNo } 可以等价写成 ${requestScope.student["studentNo"] } 或 ${requestScope["student"]["studentNo"] }。除此之外,中括号[]操作符还有以下一些其他独有功能。

  • 如果属性名称中包含一些特殊字符,如“.”、“?”、“-”等,就必须使用中括号操作符,而不能用点操作符。
  • 如果要动态取值,也必须使用中括号操作符,而不能用点操作符。
  • 访问数组。
  • 点操作符和中括号操作符还可以用来获取 Map 中的属性值。

3. 关系运算符

EL 表达式还能够进行一些简单的运算,如表所示。

关系运算符示 例结 果
大于>(或 gt)${2>1} 或 ${2 gt 1}true
大于或等于>=(或 ge)${2>=1} 或 ${2 ge 1}true
等于==(或 eq)${2==1} 或 ${2 eq 1}false
小于或等于<=(或 le)${2<=1} 或 ${2 le 1}false
小于<(或 lt)${2<1} 或 ${2 lt 1}false
不等于!=(或 ne)${2!=1} 或 ${2 ne 1}true

4. 逻辑运算符

EL 表达式能够进行的逻辑运算,如表所示。

关系运算符示 例结 果
逻辑或(或 or)`2>12<1(或 2>1 or 2<1`)true
逻辑与&&(或 and)2>1&&2<1(或 2>1 and 2<1)false
逻辑非!(或 not)!(2>1) (或 not 2>1)false

5. Empty 操作符

Empty 操作符用来判断一个值是否为 null 或不存在。

el的隐式对象

“隐式对象” 又称 “内置对象” 。之前在 JSP 里曾提到过,像 request、session、application 等都是 JSP 的隐式对象,这些 “隐式对象” 可以不用实例化就直接使用。同样的,在 EL 表达式中也存在一些隐式对象。按照使用的途径不同,EL 隐式对象分为了作用域访问对象、参数访问对象和 JSP 关联对象,如图所示。

图片描述

1. 四种作用域对象

在使用 EL 表达式来获取一个变量的同时,可以指定该变量的作用域。EL 表达式提供了四个可选的作用域对象,如表所示。

对 象 名作 用 域
pageScope把 pageContext 作用域中的数据映射为一个 Map 类的对象
requestScope把 request 作用域中的数据映射为一个 Map 类的对象
sessionScope把 session 作用域中的数据映射为一个 Map 类的对象
applicationScope把 application 作用域中的数据映射为一个 Map 类的对象

pageScope、requestScope、sessionScope 和 applicationScope 都可以看成 Map 型变量,要获取其中的数据就可以使用点操作符或中括号操作符。

2. param 对象

在 JSP 中,可以使用 request.getParameter() 和 request.getParameterValues() 来获取表单中的值(或地址栏、超链接中附带的值)。对应的,EL 表达式可以通过 param、paramValues 来获取这些值,如表所示。

对 象 名示 例作 用
param${param.username}等价于 request.getParameter("username")
paramValues${param.hobbies}等价于 request. getParameterValues ("hobbies")

3. pageContext 对象

pageContext 是 JSP 的一个隐式对象,同时也是 EL 表达式的隐式对象。因此,pageContext 是 EL 表达式与 JSP 之间的一个桥梁,是用于关联二者的对象。

在 EL 表达式中,可以通过 pageContext 来获取 JSP 的内置对象和 ServletContext 对象,如表所示。

EL 表达式获取的对象
${pageContext.page}获取 page 对象
${pageContext.request}获取 request 对象
${pageContext.response}获取 response 对象
${pageContext.session}获取 session 对象
${pageContext.out}获取 out 对象
${pageContext.exception}获取 exception 对象
${pageContext.servletContext}获取 servletContext 对象

还可以获取这些对象的 getXxx() 方法:例如 ${pageContext.request.serverPort} 就表示访问 request 对象的 getServerPort() 方法。可以发现,在使用时 EL 去掉了方法中的 get 和 “()”,并将首字母改为了小写。

JSTL 标签库

通用标签库包含了三个标签:赋值标签 <c:set>、输出标签 <c:out>、移除标签 <c:remove>。本实验主要介绍赋值标签 <c:set> 的使用。

赋值标签

<c:set> 标签的作用:给变量在某个作用域内赋值,有两个版本:“var”版和“target”版。

① var 版

用于给 page、request、session 或 application 作用域内的变量赋值。

其语法如下。

<c:set var="elementVar" value=" elementValue"  scope="scope" />

各参数的含义如下:

var:需要赋值的变量名。

value:被赋予的变量值。

scope:此变量的作用域,有四个可填项,即 page、request、session 和 application。

其示例如下。

<c:set var="addError" value="error" scope="request"/>

表示在 request 作用域内,设置一个 addError 变量,并将变量值赋值为 error,等价于

request.setAttribute("addError", "error");

② target 版

用于给 JavaBean 对象的属性或 Map 对象赋值。

a.给 JavaBean 对象的属性赋值

其语法如下。

<c:set target="objectName" property="propertyName" value="propertyValue" scope="scope"/>

各参数的含义如下:

target:需要操作的 JavaBean 对象,通常使用 EL 表达式来表示。

property:对象的属性名。

value:对象的属性值。

scope:此属性值的作用域,有四个可填项,即 page、request、session 和 application。

本实验通过以下示例,通过 Servlet 给 JavaBean 对象的属性赋值。

1.编写 Servlet

public class InitJSTLDataServlet extends HttpServlet{
    protected void doPost(HttpServletRequest request,HttpServletResponse response)
    throws ServletException, IOException    {
        //将一个Address对象赋值后,加入request作用域,并请求转发到JSP
        Address address = new Address();
        address.setHomeAddress("四川成都");
        address.setSchoolAddress("重庆");
        request.setAttribute("address", address);
        request.getRequestDispatcher("JSTLDemo.jsp").forward(request, response);
    }
}

2.编写 JSP

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<body>
    使用JSTL赋值之前:${requestScope.address.schoolAddress }
    <br/>
        <c:set target="${requestScope.address }"
        property="schoolAddress" value="**基地" />
    <br/>
    使用JSTL赋值之后:${requestScope.address.schoolAddress }
</body>

使用 JSTL 为 Map 对象赋值的语法如下所示。

<c:set target="mapName" property="mapKey" value="mapValue" scope="scope"/>

其中各参数的含义如下:

target:需要操作的 Map 对象,通常使用 EL 表达式来表示。

property:表示 Map 对象的 key。

value:表示 Map 对象的 value。

scope:此 Map 对象的作用域,有四个可填项,即 page、request、session 和 application。

以下实验,通过 Servlet 给 Map 对象的属性赋值。

public class InitJSTLDataServlet extends HttpServlet{
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException    {
        //将一个Map对象赋值后,加入request作用域,并请求转发到JSTLDemo.jsp
        Map<String,String> countries = new HashMap<String,String>();
        countries.put("cn", "中国");
        countries.put("us", "美国");
        request.setAttribute("countries", countries);
        request.getRequestDispatcher("JSTLDemo.jsp").forward(request, response);
    }
}

再使用 <c:set…/> 对 Map 对象的属性赋值,如下。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<body>
    使用JSTL赋值之前:${requestScope.countries.cn }${requestScope.countries.us }
    <br/>
        <c:set target="${requestScope.countries }" property="cn" value="中华人民共和国" />
    <br/>
    使用JSTL赋值之后:${requestScope.countries.cn }${requestScope.countries.us }<br/>
    <br/>
</body>

运行结果如图所示。

图片描述

输出标签

输出标签 <c:out> 类似于 JSP 中的 <%= %>,但功能更加强大。

其语法如下。

<c:out value="value" default="defaultValue" escapeXml="isEscape"/>

各参数的含义如下:

value:输出显示结果,可以使用 EL 表达式。

default:可选项。当 value 表示的对象不存在或为空时,默认的输出值。

escapeXml:可选项,值为 true 或 false。值为 true 时(默认情况),将 value 中的值以字符串的形式原封不动地显示出来;值为 false 时,会将内容以 HTML 渲染后的结果显示。

以下是一个示例,先在 InitJSTLDataServlet 中创建 address 对象,然后给 address 中的 schoolAddress 属性赋值,再把 address 对象放入 request 作用域,之后请求转发到 index.jsp。

1.先编写 Servlet 程序。

public class InitJSTLDataServlet extends HttpServlet{
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException    {
        Address address = new Address();
        address.setSchoolAddress("重庆");
        request.setAttribute("address", address);
        request.getRequestDispatcher("JSTLDemo.jsp").forward(request, response);
    }
}

2.再变写 JSP 页面。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<body>
    request作用域中,存放了address对象及schoolAddress属性值:
        <c:out value="${requestScope.address.schoolAddress}" />
        <br/>
    request作用域中,不存在student对象:
        <c:out value="${requestScope.student}" default="student对象为空"/>
        <br/>
    当escapeXml="true"时:
         <c:out value="<a href='https://www.baidu.com/'>百度主页</a>"
               escapeXml="true"/><br/>
    当escapeXml="false"时:
        <c:out value="<a href='https://www.baidu.com/'>百度主页</a>" escapeXml="false"/>
</body>

移除标签

可以发现,当 value 中输出的对象不存在或为空时,会输出 default 指定的默认值;当 escapeXml 为 false 时,会将 value 中的内容先渲染成 HTML 样式再输出显示。 <c:remove> 用于移除某个作用域内的变量,其语法如下。

<c:remove var="variableName" scope="scope"/>

各参数的含义如下:

var:等待被移除的变量名。

scope:变量被移除的作用域,有四个可填项:page、request、session 和 application。

以下是 <c:remove> 标签的具体使用方法,基于上一个实验中编写的 InitJSTLDataServlet 。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<body>
    并不存在的一个变量varDemo:
        <c:out value="${varDemo }"  default="不存在"/><br/>
    在request作用域内,给varDemo赋值为LanQiao
        <c:set var="varDemo" value="LanQiao" scope="request"/><br/>
    再次观察varDemo:
        <c:out value="${varDemo }"  default="不存在"/><br/>
    在request作用域内,将varDemo移除:
        <c:remove var="varDemo" scope="request"/> <br/>
    再次观察varDemo:
        <c:out value="${varDemo }"  default="不存在"/><br/>
</body>

运行结果如图所示。

图片描述

单重选择标签 <c:if>

<c:if >类似于 Java 中的 if 选择语句。

其语法如下。

<c:if test="condition"  var="variableName" scope="scope">
    代码块
</c:if>

各参数的含义如下:

test:判断条件,值为 true 或 false,通常用 EL 表达式表示。当值为 true 时才会执行代码块中的内容。

var:可选项。保存 test 的判断结果(true 或 false)。

scope:可选项。设置此变量的作用域,有四个可填项:page、request、session 和 application。

以下实验是单重选择标签的具体使用方法。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<body>
     …
    <c:if test="${3>2 }"  var="result" scope="request">
        3>2结果是:${result }
    </c:if>
</body>

程序运行结果如图所示。

图片描述

多重选择标签 <c:choose>

<c:choose >的功能类似于 Java 中 的多重 if,其语法如下。

<c:choose>
    <c:when test="">
        代码块1
     </c:when>
     <c:when test="">
         代码块2
     </c:when>
     ...
     <c:otherwise>
         代码块n
     </c:otherwise>
 </c:choose>

其中,<c:when test=""> 类似于 Java 中的判断语句:if() 和 else if();<c:otherwise >类似于多重 if 中最后的 else。具体的流程是:当<c:when> 中的 test 为 true 时,执行当前 <c:when> 标签中的代码块;如果所有 when 中的 test 都为 false,则会执行 <c:otherwise> 中的代码块。

以下实验是一个多重选择标签的具体应用。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<body>
    <c:set var="role" value="学生" />
    <c:choose>
        <c:when test="${role eq '老师' }">
            老师相关代码
        </c:when>

        <c:when test="${role eq '学生' }">
            学生相关代码
        </c:when>
        <c:otherwise>
            管理员相关代码
        </c:otherwise>
    </c:choose>
</body>

程序运行结果如图所示。

图片描述

3.迭代 <c:forEach >标签库

在 Java 之中有两种 for 循环,一种是传统的 for 循环,形式如 for(int i=0;i<10;i++),另一种是增强的 for 循环,形式如 for(String name : names),此处的 names 是字符串数组。类似的,在 JSTL 中也提供了两种 <c:forEach> 标签与之相对应,一种用于遍历集合对象的成员,另一种用于让代码重复的循环执行。

(1)遍历集合对象的成员,其语法如下。

<c:forEach var="variableName" items="collectionName"
        varStatus="variableStatusInfo" begin="beginIndex"
        end="endIndex" step="step">
        迭代集合对象的相关代码
</c:forEach>

各参数的含义如下:

var:当前对象的引用,即表示循环正在遍历的那个对象。例如,当循环遍历到第一个成员时,var 就代表第一个成员;当循环遍历到第二个成员时,var 就代表第二个成员……

items:当前循环的集合名。

varStatus:可选项。存放 var 所引用成员的相关信息,如索引号 (index) 等。

begin:可选项。遍历集合的开始位置,从 0 开始。

end:可选项。遍历集合的结束位置。

step:可选项,默认为 1。遍历集合的步长,比如当 step 为 1 时,会依次遍历第 0 个、第 1 个、第 2 个……;当 step 为 2 时,会依次遍历第 0 个、第 2 个、第 4 个……。

(2)迭代指定的次数,其语法如下。

<c:forEach var="variableName" varStatus="variableStatusInfo"
    begin="beginIndex" end="endIndex" step="step">
    循环体
</c:forEach>

其中,var、varStatus、begin、end、step 属性的含义,与“遍历集合对象的成员”中对应的属性含义相同,并且能发现此种方式的<c:forEach>缺少了 items 属性。此种方式的 <c:forEach> 主要用来让循环体执行固定的次数。

以下实验是迭代标签的具体应用示例。

a.先编写 Servlet 用于准备数据。

public class InitJSTLForeachDataServlet extends HttpServlet {
    …
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException     {
        //Address类包含家庭地址和学校地址两个属性
        Address add1 =new Address("北京朝阳区","北京大兴区");
        Address add2 =new Address("陕西西安","广州东莞");
        List<Address> addresses = new ArrayList<Address>();
        addresses.add(add1);
        addresses.add(add2);
        //addresses集合放入request作用域内
        request.setAttribute("addresses", addresses);
        request.getRequestDispatcher("JSTLDemo02.jsp").forward(request, response);
    }
}

b.使用迭代标签显示数据。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<body>
    <c:forEach var="add" items="${addresses }" varStatus="status" >
        ${status.index}:
        家庭地址:${add.homeAddress } –
        学校地址:${add.schoolAddress }<br/>
    </c:forEach>
</body>

运行结果如图所示。

图片描述

本章总结

  • EL 表达式通常由两部分组成:对象和属性。可以使用“点操作符”或“中括号 [] 操作符”来操作对象的属性,其语法格式是 ${EL 表达式}

  • 在 EL 表达式中,除了点操作符以外,还可以使用中括号操作符 [] 来访问某个对象的属性。如果属性名称中包含一些特殊字符、要访问的对象本身是一个数组、或者需要动态取值时,那么必须使用中括号操作符。

  • EL 隐式对象 又称 EL 内置对象。按照使用的途径不同,EL 隐式对象分为了作用域访问对象、参数访问对象和 JSP 关联对象,如图所示。

    图片描述

  • JSTL 包含了开发 JSP 时经常用到的一组标准标签。JSTL 一般要结合 EL 表达式一起使用。使用 JSTL 标签库以前,必须先在 pom.xml 文件中引入依赖:taglibs-standard-impl 和 jstl,然后再在需要使用。

  • JSTL 核心标签库主要包含通用标签库、条件标签库和迭代标签库。通用标签库包括赋值标签 <c:set>、输出标签 <c:out>、移除标签 <c:remove>。条件标签库包括单重选择标签 <c:if>、多重选择标签 <c:choose>