JSP,EL,JSTL详解
@TOC
一、JSP简介
1.介绍
JSP:Java Server Page(基于Java语言实现的服务器端的页面)是一种动态网页开发技术。
JSP允许在页面嵌入Java代码,可以动态展示页面。
JSP是一套规范,每个web服务器都要遵守,都是按照这个规范进行翻译。
2.学习JSP的必要性
我们来想一下,为什么要有JSP这个技术?
我们之前学过了Servlet,可以在里面写Java代码也可以写前端代码,但是你会发现我们如果只是单纯用Servlet写代码的话,代码太杂乱了,不容易维护,既有Java代码,又有前端代码,JSP的出现就是为了帮助我们解决这种问题
以后在开发中,Servlet和JSP各司其职,Servlet就用来写Java代码,JSP就负责页面展示。HTML也可以进行页面展示,为什么非得还有JSP,原因是HTML中的页面是静态的,数据是写死的,我们为了动态展示数据,就得使用JSP。
JSP是翻译引擎,根据不同的符号,把代码翻译到不同位置,服务器调用的是已经编译好的JSP文件
- JSP是java程序。(JSP本质还是一个Servlet)
- JSP是:JavaServer Pages的缩写。(基于Java语言实现的服务器端的页面。)
- Servlet是JavaEE的13个子规范之一,那么JSP也是JavaEE的13个子规范之一。
- JSP是一套规范。所有的web容器/web服务器都是遵循这套规范的,都是按照这套规范进行的“翻译”
- 每一个web容器/web服务器都会内置一个JSP翻译引擎。
3.JSP的优势
- 与纯 Servlets相比:JSP可以很方便的编写或者修改HTML网页而不用去面对大量的println语句。
- 与JavaScript相比:虽然JavaScript可以在客户端动态生成HTML,但是很难与服务器交互,因此不能提供复杂的服务,比如访问数据库和图像处理等等。
- 与静态HTML相比:静态HTML不包含动态信息。
4.JSP本质
JSP本质上是Servlet,当我们第一次访问JSP的时候,JSP引擎会 把它翻译成一个Servlet文件,再由web容器调用Servlet完成响应。
单纯从开发角度来说,JSP就是在HTML中嵌入Java程序
比如现在我们来写第一个jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
第一个jsp
</body>
</html>
项目部署完成后,访问index.jsp
- 实际上访问以上的这个:index.jsp,底层执行的是:index_jsp.class 这个java程序。
- 这个index.jsp会被tomcat翻译生成index_jsp.java文件,然后tomcat服务器又会将index_jsp.java编译生成index_jsp.class文件
- 访问index.jsp,实际上执行的是index_jsp.class中的方法。
- 下面这个图片,就是项目部署后显示在浏览器的结果
- 大家可能比较奇怪,之前用Servlet的时候,我们还需要在Servlet里面用输出方法来把html代码输出到浏览器,然后再由浏览器解析,把最终结果显示出来,为什么这里,直接就可以输出呢?
- 我们来来看看index.jsp编译后生成的文件就清楚了。
这里我使用的是Tomcat10,如果大家用的是Toncat9的话,代码编译以后文件存放位置可能和我不一样。
然后,我们把路径拷贝一下,看看编译以后生成的文件是什么样的
点击Catalina,再点击localhost
然后我们还会发现这个类继承有HttpJspBase,这个到底是什么东西呢?我们接着往下看
所以这就证明了上面所讲的JSP其实就是一个Servlet
JSP实际上就是一个Servlet。
-index.jsp访问的时候,会自动翻译生成index_jsp.java,会自动编译生成index_jsp.class,那么index_jsp
这就是一个类。
index_jsp 类继承 HttpJspBase,而HttpJspBase类继承的是HttpServlet。所以index_jsp类就是一个Servlet类。
jsp的生命周期和Servlet的生命周期完全相同。完全就是一个东西。没有任何区别。
jsp和servlet一样,都是单例的。(假单例。)
注意:对JSP进行错误调试的时候,还是要直接打开JSP文件对应的java文件,检查java代码。
5.JSP的生命周期
- 编译阶段:
servlet容器编译servlet源文件,生成servlet类
-
初始化阶段:
加载与JSP对应的servlet类,创建实例,并调用它的初始化方法 -
执行阶段:
调用与JSP对应的servlet实例的服务方法 -
销毁阶段:
调用与JSP对应的servlet实例的销毁方法,然后销毁servlet实例
很明显,JSP生命周期的四个主要阶段和servlet生命周期非常相似 ,但是也有不同的地方
jsp文件第一次访问的时候是比较慢的
- 第一次访问的时候比较麻烦:
- 要把jsp文件翻译生成java源文件
- java源文件要编译生成class字节码文件
- 然后通过class去创建servlet对象
- 然后调用servlet对象的init方法
- 最后调用servlet对象的service方法。
- 之后就比较快了
- 因为以后直接调用单例servlet对象的service方法即可。
6.JSP基础语法
在jsp文件中直接编写文字,都会自动被翻译成Java程序
翻译到servlet类的service方法的out.write(“翻译到这里”),直接翻译到双引号里,被java程序当做普通字符串打印输出到浏览器。
在JSP中编写的HTML CSS JS代码,这些代码对于JSP来说只是一个普通的字符串。但是JSP把这个普通的字符串一旦输出到浏览器,浏览器就会对HTML CSS JS进行解释执行。展现一个效果。
1.<% java语句; %>
在这个符号当中编写的被视为java程序,被翻译到Servlet类的service方法内部。
既然是被翻译到方法内部,那么我们在这里面写的代码应该是能够在方法体中写的代码
- 在service方法当中编写的代码是有顺序的,方法体当中的代码要遵循自上而下的顺序依次逐行执行。
- service方法当中不能写静态代码块,不能写方法,不能定义成员变量。。。。。。
- 在同一个JSP当中 <%%> 这个符号可以出现多个。
- 只能在这里用九大内置对象,因为内置对象是定义在_jspService方法里面的
<%-- --%>是jsp的注释
在jsp中,如果我们写的注释是html的注释格式,它的注释信息也会被翻译成Java代码
<%! %>
2.<%! %>
- 在这个符号当中编写的java程序会自动翻译到service方法之外。
- 这个语法很少用,为什么?不建议使用,因为在service方法外面写静态变量和实例变量,都会存在线程安全问题,因为JSP就是servlet,servlet是单例的,多线程并发的环境下,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题。
3.输出语句<%=%>
如果我们输出的语句有Java代码,就可以用这个语句
最终翻译成:
- 翻译成了这个java代码: out.print();
- 翻译到service方法当中了。
<%@page contentType=“text/html;charset=UTF-8”%>
可以用来解决JSP乱码问题
- page指令,通过contentType属性用来设置响应的内容类型。
7.JSP文件的扩展名
-
jsp文件的扩展名是可以配置的。不是固定的。
-
在CATALINA_HOME/conf/web.xml,在这个文件当中配置jsp文件的扩展名。
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
- xxx.jsp文件对于服务器来说,只是一个普通的文本文件,web容器会将xxx.jsp文件最终生成java程序,最终调用的是java对象相关的方法,真正执行的时候,和jsp文件就没有关系了。
- 小窍门:JSP如果看不懂,建议把jsp翻译成java代码,就能看懂了。
8.九大内置对象
- jakarta.servlet.jsp.PageContext pageContext 页面作用域
oageContext代码整个jsp页面。
通过这个对象主要用来访问页面信息
它存储了request对象和response对象的引用。application对象,config对象,session对象,out对象可以通过访问这个对象的属性来导出。
-
jakarta.servlet.http.HttpServletRequest request 请求作用域
-
jakarta.servlet.http.HttpSession session 会话作用域
-
jakarta.servlet.ServletContext application 应用作用域
- pageContext < request < session < application
- 以上四个作用域都有:setAttribute、getAttribute、removeAttribute方法。
- 以上作用域的使用原则:尽可能使用小的域。
-
java.lang.Throwable exception
-
jakarta.servlet.ServletConfig config
-
java.lang.Object page (其实是this,当前的servlet对象)
-
jakarta.servlet.jsp.JspWriter out (负责输出)
-
jakarta.servlet.http.HttpServletResponse response (负责响应)
二、EL表达式
1.基本介绍
EL表达式属于JSP,主要作用是从某个作用域中取数据,然后将其转换成字符串,然后将其输出到浏览器
EL表达式的功效。三大功效:
第一功效:从某个域中取数据。
- 四个域:
- pageContext
- request
- session
- application
- EL表达式中其他的隐式对象:
- pageContext
- param
- paramValues
- initParam
第二功效:将取出的数据转成字符串。
- 如果是一个java对象,也会自动调用java对象的toString方法将其转换成字符串。
第三功效:将字符串输出到浏览器。
- 和这个一样:<%= %>,将其输出到浏览器。
2.语法
${表达式}
3.EL表达式的使用
3.1取数据的不同情况
如果存储的数据不是对象,可以之间用表达式取出
如果存储数据是一个对象,我们想要获得对象的其中某一个属性,可以通过xxx.属性来获得
底层调用的是get方法xxx指的是我们存储到域对象的名字
${userObj}
等同于java代码:<%=request.getAttribute(“userObj”)%>
但是不要这样写:${“userObj”}
那么我们现在来说说这两个的区别
{“abc”}
表示直接将"abc"当做普通字符串输出到浏览器。不会从某个域中取数据了。
{userObj.email} 使用这个语法的前提是:User对象有getEmail()方法。
EL表达式中的. 这个语法,实际上调用了底层的getXxx()方法。
注意:如果没有对应的get方法,则出现异常。报500错误。
3.2EL表达式取出数据的顺序
在没有指定范围的前提下面,EL表达式优先从小范围取数据
pageContest<request<session<application
3.3EL表达式不会对错误数据输出
使用EL表达式的时候,当我们写错表达式的时候,它是不会输出数据的,因为EL表达式主要认为是做页面展示,要求最终页面展示上是友好的,对null做了处理
如果是null,则在浏览器显示空白
3.4EL表达式取数据的两种不同形式
- 第一种:. (大部分使用这种方式)
- 第二种:[ ] (如果存储到域的时候,这个name中含有特殊字符,可以使用 [ ])
- request.setAttribute(“abc.def”, “zhangsan”);
- ${requestScope.abc.def} 这样是无法取值的。
- 应该这样:${requestScope[“abc.def”]}
<%@ page import="com.bjpowernode.jsp.bean.User" %><%--
Created by IntelliJ IDEA.
User: 17614
Date: 2022-03-22
Time: 9:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
User user=new User();
user.setUsername("张三");
request.setAttribute("aaa",user);
request.setAttribute("bbb","hello jsp etl");
%>
<%--使用EL表达式取出 并且输出到浏览器--%>
<%--从域中取数据--%>
${aaa}<br>
<%--取user的username--%>
${aaa.username}<br>
<%--注意中括号要加双引号--%>
<%--取user的username--%>
${aaa["username"]}<br>
<%--将数据取出并输出到浏览器--%>
<%--如果我们的数据包含特殊字符就要用中括号的形式来取数据--%>
${requestScope["bbb"]}
${bbb}
3.5EL表达式从map集合取数据
${map.key}
3.6EL表达式取出数组和集合中的数据
<%@ page import="com.bjpowernode.jsp.bean.User" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %><%--
Created by IntelliJ IDEA.
User: 17614
Date: 2022-03-22
Time: 9:36
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String[] usernames={"张三","李四","王五"};
request.setAttribute("nameArray",usernames);
User u1=new User();
u1.setUsername("赵六");
User u2=new User();
u2.setUsername("王琦");
User[] users={u1,u2};
request.setAttribute("userArray",users);
List<String> list=new ArrayList<>();
list.add("abc");
list.add("def");
request.setAttribute("mylist",list);
%>
<%--取出list集合,也是通过下标取数据--%>
<%--set无法通过下标取数据--%>
${mylist}<br>
${mylist[0]}<br>
${mylist[1]}<br>
${mylist[2]}<br>
<%--取出数组中第二个用户的用户名--%>
${userArray[1].username}
<hr>
<%--使用EL表达式取出数组中的元素--%>
<%--将数组对象直接输出--%>
${nameArray}<br>
<%--取出第一个元素--%>
${nameArray[0]}<br>
<%--数组角标越界也不会报异常--%>
${nameArray[10]}
用EL表达式从数组或集合取数据
- ${数组[0]}
- ${数组[1]}
- ${list[0]}
page指令当中,有一个属性,可以忽略EL表达式
<%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
isELIgnored="true" 表示忽略EL表达式
isELIgnored="false" 表示不忽略EL表达式。(这是默认值)
isELIgnored="true" 这个是全局的控制。
可以使用反斜杠进行局部控制:\${username} 这样也可以忽略EL表达式。
3.7EL表达式获取应用根路径
<%@ page import="jakarta.servlet.http.HttpServletRequest" %><%--
Created by IntelliJ IDEA.
User: 17614
Date: 2022-03-22
Time: 10:07
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="jakarta.servlet.http.HttpServletRequest" %>
<%--
JSP有九大内置对象
pageContest,request,session,application,response,out,config,exception
其中四个域对象,最小的域是pageContext
pageContest翻译为:页面上下文对象 通过pageContest可以获取???
--%>
<%--从下面的代码来看,pageContext.getRequest()方法是没用的,JSP有内置对象request,为什么要有这个方法
因为EL表达式没有request这个隐式对象,requestScope代表的是隐请求范围,不等同于request对象
EL中有内置隐式对象pageContext这个和JSP的隐式对象pageContext是一样的--%>
<%=((HttpServletRequest)pageContext.getRequest()).getContextPath()%>
<br>
<%=pageContext.getRequest()%>
<br>
这段代码对应的EL表达式
${pageContext.request.contextPath}
<br>
<%=request%>
<br>
${pageContext.request}
<br>
获取应用根路径
<%=request.getContextPath()%>
3.8EL表达式其他隐式对象
<%--
Created by IntelliJ IDEA.
User: 17614
Date: 2022-03-22
Time: 10:48
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- JSP中EL表达式的隐含对象--%>
<%--1.pageContext--%>
<%--2.param--%>
<%--3.paramValue--%>
<%--4.initParam--%>
<%--5.其他(不常用)--%>
获取应用根路径:${pageContext.request.contextPath}
<%--request可以获取请求参数--%>
<br>
<%--用户在浏览器上提交数据:http://localhost:8080/jsp/15.jsp?username=%E5%BC%A0%E4%B8%89--%>
<%--用户名:<%=request.getParameter("username")%>--%>
<%--使用EL表达式--%>
<%--param获取的是请求参数一维数组中的第一个元素--%>
用户名:${param.username}<br>
<%--如果我们提交的是复选框,多个name相同,那要怎么办--%>
<%--如果想获取所有元素--%>
一维数组:${paramValues.hobby}<br>
一维数组:<%=request.getParameterValues("hobby")%><br>
<%--获取数组中的某一个元素--%>
<%--这样很麻烦,下标要我们自己一个个慢慢写,那有没有循环可以用呢?看后续学习--%>
hobby:${paramValues.hobby[0]}<br>
hobby:${paramValues.hobby[1]}<br>
<%--EL表达式中的隐含对象,initParam--%>
<%--ServletContext是Servlet上下文对象,对应的JSP九大内置对象之一是:application--%>
<%
String pageSize=application.getInitParameter("pageSize");
String pageNum = application.getInitParameter("pageNum");
%>
每页显示的记录条数:${initParam.pageSize}<br>
页码:${initParam.pageNum}<br>
4.EL表达式运算符
empty运算符
- [ ] empty运算符的结果是boolean类型
- [ ] ${empty param.username}
- [ ] ${not empty param.username}
- [ ] ${!empty param.password}
EL表达式中的+永远都是做运算,不会做拼接
如果无法转换成数字的话,会出现类型转换错误
+两边不是数字的话,会尝试转换成数字,如果无法转换成数字就报错,NumberFormatException
EL表达式中运用==,!=相当于是调用equals方法
三、JSTL标签库
1.介绍
通过名字我们也知道,JSTL有大量的标签
JSTL是Java的标准标签库,它通常结合EL表达式一起使用,目的是让JSP中的Java代码消失。标签写在JSP中,实际上最终还是执行Java程序
2.使用JSTL标签库步骤
①引入JSTL标签库对应的jar包
tomcat10之后引入的jar包是:
- jakarta.servlet.jsp.jstl-2.0.0.jar
- taglibs-standard-impl-1.2.5.jar
- taglibs-standard-spec-1.2.5.jar
在WEB-INF下建立lib目录,把jar包拷贝过来,然后点击add lib…
②在JSP中引入要使用的标签库(使用taglib指令引入标签库。)
JSTL有很多的标签,我们重点掌握核心标签库就可以了jstl/core是核心标签库。
③在需要使用标签的位置使用就行了,表面上使用的是标签,实际上底层还是Java程序
3.JSTL标签原理
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
以上uri后面的路径实际上指向了一个xxx.tld文件。
tld文件实际上是一个xml配置文件。
在tld文件中描述了“标签”和“java类”之间的关系。
以上核心标签库对应的tld文件是:c.tld文件。它在哪里。
在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有一个c.tld文件。
配置文件tld的解析
<tag>
<description>对该标签的描述</description>
<name>catch</name> 标签的名字
<tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class> 标签对应的java类。
<body-content>JSP</body-content> 标签体当中可以出现的内容,如果是JSP,就表示标签体中可以出现符合JSP所有语法的代码。例如EL表达式。
<attribute>
<description>
对这个属性的描述
</description>
<name>var</name> 属性名
<required>false</required> false表示该属性不是必须的。true表示该属性是必须的。
<rtexprvalue>false</rtexprvalue> 这个描述说明了该属性是否支持EL表达式。false表示不支持。true表示支持EL表达式。
</attribute>
</tag>
<c:catch var="">
JSP....
</c:catch>
4.核心标签库的常用标签
if标签
choose,when,otherwise
相当于
if(){
}else if(){
}else if(){
}
foreach标签
- 点赞
- 收藏
- 关注作者
评论(0)