目录
1 EL表达式概述
EL表达式作用:
- EL表达式可以代替JSP中的java代码,让JSP文件中的程序看起来更加美观和整洁。
- JSP夹杂着各种java代码,例如<%java代码%>,等等,导致JSP混乱后期不好维护
- EL表达式算是JSP语法的一部分,EL表达式归属于JSP
- EL表达式出现在JSP中主要是:从某个域中取数据,然后转换成字符串,然后输出到浏览器,这就是EL表达式的三大功效
EL表达式三大功效:
- 第一功效:从某个域中取数据,四个域:pageContext;request;session;application
- 第二功效:将取出的数据转成字符串,会自动调用java对象的toString方法转为字符串
- 第三功效:将字符串输出到浏览器,这个这个一样:<%=%>
EL表达式基本语法:
- ${域中的name,不加双引号}
<%--向域中存数据--%>
<%request.setAttribute("username","zhangsan");%>
<%--使用java代码从域中取数据--%>
<%=request.getAttribute("username")%>
<%--使用EL表达式取数据--%>
${username}
以上代码我们可以发现使用EL表达式和java代码作用一样但表达更方便简洁
<%
//创建一个对象
User user = new User("jack","123",30);
//存取域中
request.setAttribute("userObj",user);
%>
<%--使用EL表达式取出user数据--%>
${userObj}
<br>
<%--如果想输出对象的属性值,怎么办--%>
${userObj.username}<br>
${userObj.password}<br>
${userObj.age}<br>
${userObj.email}<br>
通过上方代码我们发现,不仅可以使用域的name输出,还可以输出属性值,不过需要注意的是:输出属性值的前提是必须要有get方法,底层调用的就是get方法,但不一定需要这个属性,就像上面的调用的email一样,我只是写了getEmail方法,并没有定义这个属性,但依然可以正常执行。但如果没有get方法就会出500错误。但经过测试我们发现,get方法不遵守驼峰命名也是可以正常访问的,但是你不遵守驼峰命名你还是java攻城狮吗???
那么问题来了,一共有四个域,假如四个域有相同的name,取出的是什么?
经过测试我们发现,优先取出域小的中的数据pageContext<request<session<application
但问题又来了,我就是想取出每一个域中的数据怎么办,EL表达式中有四个隐含的隐式的范围:
- pageScope对应的是pageContext范围
- requestScope对应的是request范围
- sessionScope对应的是session范围
- applicationScope对应的是application范围
${pageScope.data}<br>
${requestScope.data}<br>
${sessionScope.data}<br>
${applicationScope.data}<br>
假如我name写错了会怎样:
<%
request.setAttribute("username","jack");
%>
<%=request.getAttribute("username")%>
${username}
<br>
java代码写错name:<%=request.getAttribute("usernam")%>
EL表达式写错name:${usernam}
我们发现EL表达式写错什么都不会输出,而java代码写错会输出null,EL表达式实际上运行的时候还是要翻译成java代码的:
<%=request.getAttribute("username") == null ? "":request.getAttribute("username")%>
EL表达式还有一种写法:${requestScope["name"]}
这种写法什么时候用呢,比如某些人闲的没事将name其名为“user.name”,这时使用传统的“.”就会出错,将它放在中括号里的分号里避免出错,不过这种事情一般不会发生,所以基本上都是传统方法。
EL表达式获取Map集合中的元素:通过key来获取
//map集合
Map<String,String> map = new HashMap<>();
map.put("username","马邦德");
map.put("password","123456");
request.setAttribute("map",map);
User user = new User();
user.setUsername("张麻子");
Map<String, User> userMap = new HashMap<>();
userMap.put("user",user);
request.setAttribute("userMap",userMap);
<%--取出Map集合数据--%>
${map.username}<br>
${userMap.user}<br>
${userMap.user.username}<br>
EL表达式获取数组中的元素:通过下标获取,注意下标溢出就不输出任何东西
//数组
String[] s = {"jack","rose","xiaoli"};
request.setAttribute("s",s);
<%--取出数组的数据--%>
${s}<br>
${s[0]}<br>
${s[1]}<br>
${s[2]}<br>
${s[10]}<br>
EL表达式获取list集合中的元素:可通过下标获取,和数组类似
//list集合
List<String> list = new ArrayList<>();
list.add("拟好");
list.add("王德发");
request.setAttribute("list",list);
<%--取出list集合数据--%>
${list}<br>
${list[0]}<br>
${list[1]}<br>
${list[10]}<br>
page指令有个属性可以使EL表达式失效<%@page isELIgnored="false" %>,true为失效,false为不失效,假如只想某一个失效可以在EL表达式前面加上一个斜杠——“\”
通过EL表达式获取应用的根:${pageContext.request.contextPath},为什么不直接使用request来获取,因为EL表达式中没有request这个对象。
EL表达式其它隐式对象:
- param
- paramValues
- initParam
param可以代替request.getParameter("username")取出前端数据,格式为:${param.username}但这只能取出一个数据,也就是说当数据有多个只能取出第一个数据。paramValues可以解决这个问题,格式为:{paramValues.username[0]},但是目前也只能根据下标一次取一个。initParam可以代替application.getInitParameter("pageSize")取出应用域数据,格式为${initParam.pageSize}应用域的数据在web.xml文件存储方式如下:
<!--servlet上下文初始化参数-->
<context-param>
<param-name>pageSize</param-name>
<param-value>20</param-value>
</context-param>
关于EL表达式的运算符
- “+”:只能运算数字,不能连接字符串,连接字符串就会报错
- “==”:会自动调用equal方法,比较的就是你长得样子,而不是内存地址
- “eq”:作用与“==”一样
- “!、not”:取反
- “empty”:判断是否为空,如果是空就返回true
了解一下EL表达式运算符即可,EL表达式重点在于输出内容
2 JSTL标签
什么是JSTL标签库?
- Java Standard Tag Lib(Java标准的标签库)
- JSTL标签库通常结合EL表达式一起使用,目的是让JSP中的java代码消失。
- 标签是在JSP中写的,但实际上最终还是要执行对应的java程序(java程序在jar包中)
使用JSTL标签库的步骤:
第一步:引入JSTL标签库对应的jar包:
- jakarta.servlet.jsp.jstl-2.0.0.jar、jakarta.servlet.jsp.jstl-api-2.0.0.jar
- 在IDEA怎么引入:在WEB——INF目录下新建lib目录,然后jar包拷贝到lib中,然后将其“Add Lib”一定要和MySQL数据库的驱动一样,都是放在那个目录。什么时候需要放到lib下呢?如果这个jar是tomcat没有的就需要。
第二步:在JSP中引入要是用的标签库(使用taglib指令引入标签库)
- <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 这个是核心标签库,prefix随便起名字,一般起为c
第三步:在需要使用标签的位置使用即可,表面使用的标签,底层实际上还是java程序
JSTL标签原理:
- <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 以上uri后面的路径实际上指向了一个xxx.tld文件,tld文件中描述了“标签”和“java类”之间的关系
- 以上核心标签库对应的tld文件是:c.tld文件,它在哪:
- 在Jakarta.servlet.jsp.jstl=2.0.0.0.jar里面META-INF目录下
源码解析如下:
<description>
这里是对标签的描述
</description>
<name>forEach</name>标签的名字
<tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class>标签对应的java类
<tei-class>org.apache.taglibs.standard.tei.ForEachTEI</tei-class>
<body-content>JSP</body-content>标签中可以出现的内容,如果是JSP,就表示标签中可以写JSP
<attribute>
<description>
对这个属性的描述
</description>
<name>items</name>属性名
<required>false</required>false表示属性可有可无,true表示属性必须有
<rtexprvalue>true</rtexprvalue>true表示支持EL表达式,false不支持
<type>java.lang.Object</type>
<deferred-value>
<type>java.lang.Object</type>
</deferred-value>
</attribute>
<attribute>
案例:
forEach标签:
我写一个学生类,并创建三个对象附上属性值,并放入到list集合,再将集合放到request域中,使用JSTL标签和EL表达式取出这里面的数据:
<c:forEach items="${Stulist}" var="s">
name:${s.name},age:${s.age}<br>
</c:forEach>
上面这些代码就可以取出,其中forEach是一个标签,这个标签的作用就类似于for循环,item是这个标签的属性,这个属性里面装入集合,可以用EL表达式拿到存在域中的集合,然后var属性表示集合中的每一个元素,就类似于加强for循环中的那个元素。
<c:forEach items="${Stulist}" var="s" varStatus="stuStatus">
编号:${stuStatus.count},name:${s.name},age:${s.age}<br>
</c:forEach>
其中varStatus这个属性表示var的状态,这个名字可以随便起,这个对象中有count属性,可以直接用,这个属性的值是从1开始,以1递增,主要用于编号:
<c:forEach var="i" begin="1" end="10" step="1">
${i}、
</c:forEach>
其中var指定循环的变量相当于for循环前的的int i,begin为开始,end为结束,step为步长,和for循环都有对应。需要注意的是:底层实际上会将i存储到pageContext域中,因为取数据使用EL表达式取得,而EL表达式取出的数据一定是域中的。
if标签:
<c:if test="${empty param.username}">
<h1>用户名不能为空</h1>
</c:if>
<c:if test="${not empty param.username}">
${param.username}
</c:if>
需要注意的是:if属性中的test属性必须要有,并且text属性只能是Boolean类型。
choose标签:就类似于if-else或者switch
<c:choose>
<c:when test="${param.age < 18}">
青少年
</c:when>
<c:when test="${param.age < 35}">
青年
</c:when>
<c:when test="${param.age < 60}">
中年
</c:when>
<c:otherwise>
老年
</c:otherwise>
</c:choose>
3 改造oa项目
使用EL表达式和JSTL标签继续改造oa项目
首先在jsp页面将java代码全用EL表达式和JSTL标签代替,代替也很简单,基本上就是路径的代替,list页面稍微复杂一点,需要使用JSTL标签。
还有我们发现在前端需要写不少项目的根路径,有点麻烦,有没有方便的方式:在前端HTML代码中有一个标签叫做base,这个标签可以设置整个网页的基础路径,这是HTML中的语法,通常出现在head标签中:<base href="http://localhost:8080/oa3/">,这样设置之后,在页面中凡是没有以“/”开始的,都会自动将base中的路径添加到这些路径之前。但需要注意的是:在JS代码中的路径,保险起见,最好不要依赖base标签,JS代码按之前的方法写。但这个路径是写死的,我们不喜欢写死,可以写一个动态的:
<base href="${pageContext.request.scheme}://${pageContext.request.serverName}: ${pageContext.request.serverPort}${pageContext.request.contextPath}/">