目录

一:深入学习EL表达式

1、什么是EL表达式

2、EL表达式的主要作用

3、EL表达式的使用

4、面试题 :${abc} 和 ${"abc"}的区别

5、EL表达式读取数据的顺序

6、EL表达式对null进行了预处理

7、EL表达式取数据的时候有两种形式

8、从Map集合中取数据

9、从数组和List集合中取数据

10、设置忽略EL表达式

11、通过EL表达式获取应用的根

12、常用的隐式对象

13、EL表达式的运算符


一:深入学习EL表达式

1、什么是EL表达式

①EL表达式:Expression Language(表达式语言) 。

EL表达式可以代替JSP中的java代码,让JSP文件中的程序看起来更加整洁,美观。

③JSP中夹杂着各种java代码,例如<% java代码 %>、<%=%>等,导致JSP文件很混乱,不好看,不好维护!所以才有了后期的EL表达式。

EL表达式可以算是JSP语法的一部分,EL表达式归属于JSP

2、EL表达式的主要作用

从某个作用域中取数据,然后将其转换成字符串,然后将其输出到浏览器。这就是EL表达式的功效;三大功效:

第一功效:从某个域中取数据。 四个域:pageContext、request、session、application

第二功效:将取出的数据转成字符串。如果是一个java对象,也会自动调用java对象的toString方法将其转换成字符串。

第三功效:将字符串输出到浏览器。和<%= %>效果一样,将其输出到浏览器。

3、EL表达式的使用

${表达式}

①注意:EL表达式只负责取,不负责存。

②我们来看下面一个例子,调用 request的setAttribute("username","zhangsan");方法存入数据;以前取的话是使用<%=request.getAttribute("username")%>这种方式取;但是现在使用EL表达式只需要简介的一句话:${username}。

<%@page contentType="text/html;charset=UTF-8" %>

<%
    // 向request作用域当中存储数据
    request.setAttribute("username","zhangsan");
%>

<%--将request域当中的数据取出来,并且还要输出到浏览器,使用java代码的方式--%>
<%=request.getAttribute("username")%>

<%--使用EL表达式--%>
${username}

①创建一个完整的javabean对象

package com.bjpowernode.javaweb.jsp;

import java.util.Objects;

/**
 * 
 * @Author:朗朗乾坤
 * @Package:com.bjpowernode.oa.bean
 * @Project:JavaWeb
 * @name:Dept
 * @Date:2022/11/28 10:59
 */

public class Dept {
    private String deptno;
    private String dname;
    private String loc;
    // 构造方法
    public Dept() {
    }
    public Dept(String deptno, String dname, String loc) {
        this.deptno = deptno;
        this.dname = dname;
        this.loc = loc;
    }
    // setter and getter
    public String getDeptno() {
        return deptno;
    }
    public void setDeptno(String deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }
    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }
    public void setLoc(String loc) {
        this.loc = loc;
    }

    // 重写toString方法
    @Override
    public String toString() {
        return "Dept{" +
                "deptno='" + deptno + '\'' +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }

    // 重写equals方法 和 hashCode方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Dept dept = (Dept) o;
        return Objects.equals(deptno, dept.deptno) &&
                Objects.equals(dname, dept.dname) &&
                Objects.equals(loc, dept.loc);
    }

    @Override
    public int hashCode() {
        return Objects.hash(deptno, dname, loc);
    }
}

②把这个java对象放入到request域当中,并使用EL表达式取出

😊如果没有重写toString方法:com.bjpowernode.javaweb.jsp.Dept@38039c48

😊如果重写了toString方法:Dept{deptno='10', dname='研发部', loc='北京'}

<%@ page import="com.bjpowernode.javaweb.jsp.Dept" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<%
    // 创建一个java对象
    Dept dept = new Dept();
    dept.setDeptno("10");
    dept.setDname("研发部");
    dept.setLoc("北京");
    // 将这个对象放到request域当中
    request.setAttribute("userObj",dept);
%>
${userObj}

那如果想取每个特定的属性呢?

😊不需要调用Dept类的对应get方法去获取得;而是直接调用属性名(暂时这样理解);实际上直接调用属性名,底层自己会调用对应的get方法!

😊${userObj} 底层是怎么做的?从域中取数据,取出user对象,然后调用user对象的toString方法,转换成字符串,输出到浏览器。

${userObj.deptno}  10 
${userObj.dname}   研发部 
${userObj.loc}     北京

😊注:实际上和属性名并没有太大的关系,只和get方法有关,例如:getDeptno()是把get和()都去掉,Deptno换成全小写的deptno,下面通过一个例子进行验证:

// 在Dept类中新增加一个getEmail()方法,但是没有声明email属性
 public String getEmail() { return "123.com"; }
// 使用.email也是能访问到的
${userObj.email} // 123.com

😊所以如果想输出对象的属性值:

{userObj.deptno} 使用这个语法的前提是:User对象有getDeptno()方法。
${userObj.dname} 使用这个语法的前提是:User对象有getDname()方法。
${userObj.loc} 使用这个语法的前提是:User对象有geLoc()方法。
${userObj.email} 使用这个语法的前提是:User对象有getEmail()方法。
😊EL表达式中的. 这个语法,实际上调用了底层的getXxx()方法。

😊所以可以通过EL表达式${userObj.addr.zipcode}猜测对应的java代码
dept.getAddr().getZipcode()

😊注意:如果没有对应的get方法,则出现异常。报500错误。

4、面试题 :${abc} 和 ${"abc"}的区别

①${abc}表示从某个域中取出数据,并且被取的这个数据的name是"abc",之前一定有这样的代码: 域.setAttribute("abc", 对象);
②${"abc"} 表示直接将"abc"当做普通字符串输出到浏览器,不会从某个域中取数据了。

5、EL表达式读取数据的顺序

①EL表达式优先从小范围中读取数据。

②pageContext < request < session < application

<%
    // 四个域都存储了数据,并且name相同。
    session.setAttribute("data", "session");
    request.setAttribute("data", "request");
    pageContext.setAttribute("data", "pageContext");
    application.setAttribute("data", "application");
%>

<%--在没有指定范围的前提下,EL表达式优先从小范围中取数据--%>
<%--pageContext < request < session < application --%>
${data}

③在EL表达式中可以指定范围来读取数据;EL表达式有4个隐含的隐式的范围对象:

😊pageScope 对应的是 pageContext范围。

😊requestScope 对应的是 request范围。

😊sessionScope 对应的是 session范围。

😊applicationScope 对应的是 application范围。

<%--以下是指定范围取数据--%>
${pageScope.data}<br>
${requestScope.data}<br>
${sessionScope.data}<br>
${applicationScope.data}<br>

6、EL表达式对null进行了预处理

①EL表达式主要任务是做页面展示,要求最终页面展示上是友好的。

②所以EL表达式对null进行了处理。如果是null,则在浏览器上显示空白。

③EL表达式表面上是这种写法,实际上运行的时候,还是要翻译生成java代码的。

④${usernam} 这个EL表达式等同于三目运算符这一段java代码:<%=request.getAttribute("usernam") == null ? "" : request.getAttribute("usernam")%>

<%@page contentType="text/html;charset=UTF-8" %>

<%
    request.setAttribute("username", "zhangsan");
%>

<%--取出数据并且输出到浏览器,都能正常输出到浏览器--%>
<%=request.getAttribute("username")%>
采用EL表达式:${username}

<%--故意写错,前者取出来的是null,后者显示的是空白--%>
<%=request.getAttribute("usernam")%>
采用EL表达式:${usernam}

7、EL表达式取数据的时候有两种形式

①前面我们已经学习了"."的形式取出数据

例如:${dept.dname}就等于${dept["dname"]},注意以"[]"形式取,一定要加上双引号[""]

②对于存在特殊字符的变量名,只能使用[""]的方式取

<%
    // 向request域当中存储数据。
    request.setAttribute("abc.def", "hello");    
%>

<%--将数据取出并输出到浏览器--%>
${requestScope.abc.def} 这样是无法取值的
${requestScope["abc.def"]} 可以正常取值

8、从Map集合中取数据

语法格式:${map.key}

①创建一个Map集合,直接放入对应的数据后,在把Map集合放入request域

<%
     // 创建一个Map集合
    Map<String,String> map = new HashMap<>();
    // 存入数据到map集合
    map.put("username", "zhangsan");
    map.put("password", "123");
    // 将Map集合存储到request域当中。
    request.setAttribute("userMap", map);
%>

<%--使用EL表达式,将map中的数据取出,并输出到浏览器--%>
${userMap.username}
${userMap["username"]}

②先创建一个Map集合;在创建一个User类,给对应的属性赋值后把User类放入Map集合,最后在把Map集合放入request域,怎么取数据?

    // 创建map集合
    Map<String,User> userMap = new HashMap<>();
    // 创建user类
    User user = new User();
    // 给user类的name属性设置值
    user.setUsername("lisi");
    // 把user类放入map集合
    userMap.put("user", user);
    // 将Map集合存储到request域当中
    request.setAttribute("map", userMap);
%>

<%--使用EL表达式,将map中的数据取出,并输出到浏览器--%>
${map.user.username}

9、从数组和List集合中取数据

①对于数组和List集合中取数据,都可以采用下标的形式进行取:

对于数组:${数组[0]}

<%
    // 数组对象
    String[] usernames = {"zhangsan", "lisi", "wangwu"};
    // 向request域中存储数组
    request.setAttribute("nameArray", usernames);
%>

<%--使用EL表达式取出数组中的元素--%>
${nameArray} <%--将数组对象直接输出--%>
${nameArray[0]} <%--取出数组中的第一个元素--%>
${nameArray[1]}
${nameArray[2]}

对于list集合:${list[0]}

<%
    //创建一个list集合
    List<String> list = new ArrayList<>();
    // 调用add方法,添加数据
    list.add("abc");
    list.add("def");
    // 把list集合放入request域
    request.setAttribute("myList", list);
%>

<%--取出List集合--%>
<%--list集合也是通过下标的方式取数据的。--%>
${myList}
${myList[0]}
${myList[1]}

10、设置忽略EL表达式

①page指令当中,有一个isELIgnored属性,可以忽略EL表达式;

②默认是不忽略了,如果设置忽略,写出的EL表达式会被当做普通字符串输出 

③isELIgnored="true" 表示忽略JSP中整个页面的所有EL表达式。如果想忽略其中某个,可以使用反斜杠"\"!

<%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
isELIgnored="true" 表示忽略EL表达式
isELIgnored="false" 表示不忽略EL表达式。(这是默认值)

isELIgnored="true" 这个是全局的控制。
可以使用反斜杠进行局部控制:\${username} 。

11、通过EL表达式获取应用的根

①我们已经知道JSP有九大内置对象:pageContext,request,session,application,response,out,config,page,exception;其中四个域对象,其中最小的域是pageContext(页面上下文),通过pageContext可以获取应用的根

②在内置对象pageContext中有一个getRequest()方法,可以用来获取到request对象;但是JSP中九大内置对象中已经包含了request对象;所以看起来这个方法没有什么用!

③实际上在EL表达式当中没有request这个隐式对象,前面讲的requestScope 这个只代表“请求范围”,不等同于request对象;但是在EL表达式当中有一个隐式的对象:pageContext, EL表达式中的pageContext和JSP中的九大内置对象pageContext是同一个对象;这就很有意思了;所以在EL表达式中虽然没有request隐式对象,但是可以通过隐式对象pageContex获取到request对象;就可以写出下面等价的代码:

// 在JSP中pageContext内置对象调用getRequest()方法获取到request对象
<%=pageContext.getRequest() %>
// 对应到EL表达式中pageContext隐式对象的代码就是
${pageContext.request}

④所以获取到应用的根,就可以通过EL表达式获取:${pageContext.request.contextPath}

// 直接通过request对象获取应用的根
<%=request.getContextPath()%>
// 先通过pageContext对象获取到request对象,在获取应用的根
// 注意:这里强转是因为getContextPath()是HttpServletRequest对象的方法,而先获取到的request对象
<%=((HttpServletRequest)pageContext.getRequest()).getContextPath() %>
// 上面对应的EL表达式
${pageContext.request.contextPath}

12、常用的隐式对象

(1)pageContext

①这个隐式对象已经讲过了,它是和JSP九大内置对象的pageContex是同一个对象;最主要的用法就是用来获取应用的根:${pageContext.request.contextPath}

(2)param

①假如前端发送请求(通过form表单或者地址栏上直接提交数据):http://localhost:8080/jsp/15.jsp?username=lisi 我们要想获得前端提交的数据,就要调用内置对象request的getParameter方法;那如果使用EL表达式呢?需要使用隐含对象"param.用户名"的形式:

用户名:<%=request.getParameter("username")%><br>
用户名:${param.username}<br>

 (3)paramValues

①那如果提交的数据是一个复选框(checkbox)呢?同一组的checkbox的name是一样的;例如:http://localhost:8080/jsp/15.jsp?aihao=smoke&aihao=drink&aihao=tangtou--%>;此时在按照原来的方式进行访问,获取的是一维数组当中的第一个元素,这就需要paramValues 隐式对象了!

<%--param 获取的是请求参数一维数组当中的第一个元素--%>
爱好:${param.aihao} <br>
爱好:<%=request.getParameter("aihao")%> <br>

②使用paramValues获取的是一个字符串的一维数组

${paramValues.aihao}<br>
<%=request.getParameterValues("aihao")%><br>

③得到这个一维数组后,就可以通过下标的方式进行获取数组中的元素! 

爱好:${paramValues.aihao[0]}、${paramValues.aihao[1]}、${paramValues.aihao[2]} <br>

 (4)initParam

①ServletContext是Servlet上下文对象,对应的JSP九大内置对象之一是:application

②取出的是<context-param>标签中配置的属性,属于上下文初始化参数(全局的)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--Servlet上下文初始化参数-->
    <!--上下文初始化参数被封装到:ServletContext对象当中了。-->
    <context-param>
        <param-name>pageSize</param-name>
        <param-value>20</param-value>
    </context-param>

    <context-param>
        <param-name>pageNum</param-name>
        <param-value>5</param-value>
    </context-param>
</web-app>
③web.xml文件配置好以后进行输出
每页显示的记录条数:<%=application.getInitParameter("pageSize")%> <br>
页码:<%=application.getInitParameter("pageNum")%> <br>
-------------------------等价于---------------------------------
每页显示的记录条数:${initParam.pageSize} <br>
页码:${initParam.pageNum} <br>

13、EL表达式的运算符

(1)算术运算符:+(重点)、-、*、/、%

+在这里只做求和运算,不会做字符串拼接;如果是数字类型的字符串会自动转换为数字进行运算;如果不是数字类型的字符串会报500错误,并抛出NumberFormatException异常。

${10 + 20} <br> 30

<%-- 在EL表达式当中“+”运算符只能做求和运算;不会进行字符串拼接操作 --%>
<%--"20"会被自动转换成数字20--%> 
${10 + "20"} <br> 30

<%-- java.lang.NumberFormatException: For input string: "abc" --%>
<%-- + 两边不是数字的时候,一定会转成数字。转不成数字就报错:NumberFormatException--%>
${10 + "abc"} <br> 500错误

(2)关系运算符:== 、eq(相当于==,重点)、 !=、 >、 >=、 <、 <=

使用==实际上会自动调用equals方法;所以比较的是内容,不是内存地址。实际上eq、!=底层也会调用equals方法。

${"abc" == "abc"} <br>  true
${"abc"} == ${"abc"} <br>   abc == abc

<%
    Object obj = new Object();
    request.setAttribute("k1", obj);
    request.setAttribute("k2", obj);
%>
${k1 == k2} <br>  true


<%
    Object o1 = new Object();
    Object o2 = new Object();
    request.setAttribute("o1", o1);
    request.setAttribute("o2", o2);
%>
${o1 == o2} <br> false

<%
    String s1 = new String("hehe");
    String s2 = new String("hehe");
    request.setAttribute("a", s1);
    request.setAttribute("b", s2);
%>
${a == b}  <br>  true

(3)逻辑运算符:! && || not and or

! 和 not 效果一样的,都是取反。

Student stu1 = new Student("110", "警察");
Student stu2 = new Student("110", "警察");
${!(stu1 eq stu2)}<br> false
${not(stu1 eq stu2)}<br> false

(4)取值运算符:[ ]和.

前面代码经常在用了,就是取值的作用。

(5)empty运算符 (重点)

empty运算符的结果是boolean类型 ;判断是否为空,如果为空,结果是true。如果不为空,结果是false。

${empty param.username} <br>
${!empty param.username} <br> 取反
${not empty param.username} <br> 取反


<%--前提:访问的时候,地址栏上没有给password传参--%>
${param.password == null} <br> true 
<%--前半部分是boolean类型,后面是null--%>
${empty param.password == null} <br> false

(6)条件运算符:? :

和empty联合使用,可以实现一个很简单的逻辑业务。

${empty param.username ? "对不起,用户名不能为空!" : "欢迎访问!!!!" }

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐