一、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}
表示从某个域中取出数据,并且被取的这个数据的name是"abc",之前一定有这样的代码:
域.setAttribute(“abc”, 对象);

${“abc”}
表示直接将"abc"当做普通字符串输出到浏览器。不会从某个域中取数据了。
${userObj}
底层是怎么做的?从域中取数据,取出user对象,然后调用user对象的toString方法,转换成字符串,输出到浏览器。
${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--%>
<%--ServletContextServlet上下文对象,对应的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标签

在这里插入图片描述
在这里插入图片描述

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐