深入了解 JSP 的运行过程

JSP(全称JavaServer Pages)是由 Sun Microsystems 公司主导创建的一种==动态==网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成 HTML、XML 或其他格式文档的 Web 网页,然后返回给请求者。

JSP 主要作用

  • 代替 Servlet 程序回传 HTML 页面的数据。

接下来我们去看看 JSP 是如何实现代替 Servlet 的。

如果我们访问一个 Servlet 程序,让它返回给我们一个前端页面,那么就需要在 JavaWEB 项目下编写一个 HelloServlet 程序:

当我们启动程序,在浏览器访问http://localhost:8080/hello-servlet 时,浏览器会回传显示一个简单的页面

按 F12 进入开发者模式,可以看到 Servlet 程序响应回传了我们在Servlet 程序中拼接的 html 代码。

那么到这里还不能让我们明白 JSP 就是一个 Servlet,再往下看。

我们创建一个JSP 文件 index.jsp,这样启动项目时,自动弹出的首页就是我们的 index.jsp 页面。

我们启动项目

从控制台找到项目部署路径

我们直接访问此路径,可以看到目录下存在的文件。

进入work\\Catalina\\localhost\\ROOT\\org\\apache\\jsp 目录,可以看到存在的两个文件【index_jsp.java】和【index_jsp.class】

接着我们将这两个文件删除,同时在浏览器进行刷新页面,可以看到目录下新增了我们刚删除的两个文件【index_jsp.java】和【index_jsp.class】

接着,我们使用 Notepad++ 打开【index_jsp.java】文件,查看编译之后的 Java 文件代码。

可以看到 index.jsp 中 HTML 部分的代码被嵌入在 java 代码中,这和我们一开始在 HelloServlet 中拼接的 HTML 代码一摸一样。

查看源码我们可以看见 index_jsp 继承了 org.apache.jasper.runtime.HttpJspBase 类,

我们继续查看 org.apache.jasper.runtime.HttpJspBase 类的源码,可以发现,HttpJspBase 类直接继承了 HttpServlet类。也就是说,JSP 翻译出来的 index_jsp.java 类,它间接了继承 HttpServlet 类,这和我们直接编写 Servlet 程序继承 HttpServlet 类 是一样的。

因此 JSP 本质就是一个 Servlet 程序。它可以省掉不少我们在 Servlet 中拼接 HTML 代码的时间。不过随着前后端分离的出现,JSP 也逐渐被淘汰。

当我们第一次访问 JSP 页面的时候,Tomcat 服务器主要会帮我们完成:

  1. 把 JSP 页面翻译成为一个 .java 源文件
  2. 对 .java 源文件编译成为 .class 字节码文件
  3. 执行 .class 字节码文件返回结果

当我们第二次访问 JSP 页面的时候,如果未做更改,则使用上次编译的 .class 文件;若修改过,则重新编译。

深入理解JSP

在前面的登录案例中,登录失败后为了能够响应登录的错误信息。我特意创建了一个LoginErrorServlet用来动态地拼接错误信息。

【代码如下】

  1. LoginErrorServlet:登录失败,手动拼接login.html页面,并将错误信息动态地添加进去。

2. LoginErrorServlet:手动拼接html页面,动态展示登录错误信息。

说明:上述虽然能够达到我的需求。但是他有以下两个缺点:

  1. 拼接麻烦开发效率低;
  2. 阅读性差,代码难以维护;

问题:思考既然以前在servlet有上述缺点,为什么还要使用拼接呢?

因为如果我们想在html页面显示错误的信息,我们只能采用这种方式,html页面不能够书写java代码。而我们既不采用上述拼接标签的方式,还想实现简单的效果。我们可以使用jsp技术,在jsp中可以书写java代码同时还可以书写html标签。这样 就能够使用jsp技术在客户端和服务端直接进行数据交互了。

JSP全名为Java Server Pages,中文名叫java服务器页面,其本质是一个Servlet ,它是由Sun 公司倡导、许多公司参与一起建立的一种动态网页 技术标准。其实就是一个既能书写Servlet代码又能书写HTML代码的文件。

【图1 创建JSP】

文件内容如下:

jsp中既可以书写java代码,同时还可以书写html标签。我们完成练习如何在jsp页面中书写java代码。

【需求】

  1. 在jsp页面上获取当前日期,并将其格式化成字符串\”yyyy-MM-dd HH:mm:ss\”;
  2. 将这个字符串展示在浏览器上;

说明:

1)jsp中的注释:<%–注释内容–%>,添加注释的快捷键是:ctrl+/;

2)在jsp中书写代码有三种方式,我们这里先介绍一种,格式:

<%java代码%>

【参考代码】demo01.jsp

【运行结果】启动tomcat,并且访问这个jsp页面。

在浏览器看到 format格式的日期:

【控制台输出】

通过上面的jsp初体验, 我们看到jsp中既可以编写java代码也可以直接编写html代码,相对servlet更加方便.并且我们通过浏览器访问到的jsp页面,最后在浏览器中看到了响应到浏览器中的结果。那么整个过程的执行流程是怎样的呢,我们可以参考如下图:

通过上面的执行流程,我们可以找到jsp生成的.java源文件,这样就可以知道jsp的真正原理,同时可以看下jsp为什么是servlet。

如何查看生成的.java源文件呢?

我们可以到tomcat中查看一下上面案例中的jsp页面是怎么样的一种存在。IDEA借助tomcat发布web项目的机制:动态发布(为每一个web项目创建一个单独的发布文件)。我们可以通过tomcat其中日志中的CATALINA_BASE路径找到我们当前这个JSP页面在tomcat中的位置:

【图1 IDEA发布项目的路径】

找到这个目录后,会看到以下3个文件夹:

【图2 文件目录】

我们打开C:\\Users\\admin.IntelliJIdea2017.3\\system\\tomcat\\Tomcat_8_5_311_day05目录发现有两个文件:

【图3 JSP被翻译后的代码】

打开demo01_jsp.java文件后,代码如下所示:

我们可以看到当前的jsp文件被翻译成了一个类,这个类继承HttpJspBase类,那么这个HttpJspBase类又是什么?

注意:jsp的翻译由服务器完成,HttpJspBase类一定也是tomcat服务器的内容,所以我们需要找到org.apache.jasper.runtime.HttpJspBase这个类,这个类可以到tomcat的安装目录下的lib文件夹下找到jasper.jar,将其解压,找如下目录:org\\apache\\jasper\\runtime,找到HttpJspBase.class。

使用反编译工具打开,如下所示:

通过观察源码,根据我们所学习的继承关系,我们发现JSP其实底层就是一个servlet。通过观察源码,我们发现我们刚刚编写的所有代码都在该Servlet里面的service方法内部。

总结:

1.<% %>中书写的代码被直接解析成java代码;

2.jsp之所以可以编写html代码,其实本质上也是类似我们使用Servlet直接输出的。也就是说底层还是像我们之前做法一样进行标签拼接。html部分都被out.write(\”\”)方法以字符串的形式拼接,然后响应给浏览器;

3.在这个java文件中有个_jspService,这个方法有两个参数request,response;

4.由于我们自己编写的代码全部都落入到了service方法内部一开始就已经声明了request,response,session,application(ServletContext)等对象了,这些对象成为之jsp内置对象。

在JSP页面的body标签中,可以直接书写html代码和JS代码。但是,如果在JSP页面中书写java代码。必须遵循固定的格式,才能够生效;JSP页面中书写java代码有三种方式:1.脚本片段;2.脚本声明;3.脚本表达式;

脚本片段指的是一段java代码。书写格式:<% java 代码 %>

【示例】 demo02.jsp

【被翻译后的源码】

【页面结果】:

说明:通过查看jsp的源代码我们发现,脚本片段的代码都会存在service方法中,而方法中是不可以定义一个方法的。

脚本声明的格式:<%! 书写Java代码 %>

【示例一】声明成员变量 demo03.jsp

【被翻译后的代码】成员变量

虽然脚本声明和脚本片段已经可以书写Java代码了,但是如果我们要使用java代码向页面输出一些内容,还是需要 使用原来的response对象,比较繁琐,因此,我们需要一个更加简便的方式,可以代替response向页面输出内容 ——这个就是脚本表达式。

脚本表达式的格式:<%= 向浏览器输出的内容 %> 等价于:out.print( 向浏览器输出的内容)

【示例】在页面输出信息

注意:

上面代码中,我们书写代码的顺序是先:

再:

可是浏览器查看数据的结果是

原因:response.getWriter()获取的输出流是:PrintWriter。而脚本表达式<%=%> 等价于:out.print( 向浏览器输出的内容).并且这种方式获取的输出流是:JspWriter。其实导致上述输出结果前后顺序就是PrintWriter和JspWriter。

JspWriter:将输出的内容先放到jsp内置的缓冲区中,然后再刷新输出。

PrintWriter:是不使用jsp内置的缓冲区,直接将内容写到网页中。

总结:

1.不要同时使用脚本表达式和response同时往页面输出信息,会出现顺序不一致。现象:response会出现在最前面。使用一种输出即可。

2.脚本表达式<%= str %> :在页面输出内容,在service方法中起作用;

3.脚本片段<% %> :在service方法中,原样输出的代码片段;属于局部为,放在_jspService方法中

4.脚本声明:<%! String str = \”程序员\” %> :定义成员变量;

JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出效果,而只是告诉引擎如何处理JSP页面中的其余部分。

指令用来声明JSP页面的一些属性,比如编码方式,文档类型。我们在servlet中也会声明我们使用的编码方式和响应的文档类型的,而JSP就是用指令来声明的。

【作用】

【格式】

【作用】

【格式】

【使用示例】

需求:课后的原型中的页面放到web文件夹下面。然后打开主页index.jsp.在主页中引入header.jsp页面。

【效果】

三大指令是什么?分别的作用是?

  1. page:设置网页上一些属性
  2. taglib:导入标签库
  3. include:包含另一个JSP页面

page是jsp中必须使用的一个指令,用于设置JSP上各种页面的属性,告诉tomcat如何将一个JSP翻译成Servlet

可以放在JSP中任何位置,一般建议放在页面的最顶部

方式一: 每个page指令导入一个类

方式二: 一个page指令的import属性导入所有的包,包之间使用逗号分隔

作用:相当于导包

【代码演示】

【作用】设置响应编码

1、通过page指令的errorPage属性指定:如果页面出错,转发到error.jsp这个页面

代码演示:

页面效果:

2、通过web.xml文件指定错误码:响应状态码为404则跳转到404.jsp页面

如果不指定,那么按照以前报错方式,会出现如下错误页面:

我们可以在web.xml进行配置跳转到更加好看一些的页面。

404.jsp页面代码:

3、通过web.xml文件指定错误类型:发生空指针,则跳转到null.jsp页面

demo02.jsp

报空指针异常后的页面:null.jsp

浏览器效果:

JSP动作标签利用XML语法格式的标记来控制Servlet引擎的行为 。利用JSP动作标签可以动态包含其他jsp页面、把用户跳转到另外的页面、为Java插件生成HTML代码。

JSP页面动作标签很多,常用的主要有以下3种:

【作用】

【语法】

【使用示例】

a.jsp

b.jsp

【动态包含和静态包含】

说明:

如果只是引入jsp,不需要改变,那么使用静态引入。如果引入的jsp页面是变化的,那么使用动态引入。

用于页面的转发,与request.getRequestDispatcher(\”/URL\”).foward(request,response);功能一样的

用于给<jsp:forward>和<jsp:include>提供参数

  1. forward作用
  2. param的功能
  1. 功能:用于转发,相当于request.getRequestDispatcher(\”/URL\”).forward(request, response)
  2. 语法
  1. 功能:给forward和include提供参数名和值
  2. 语法:做为子标签存在
  3. 需求:在c.jsp页面中使用转发标签forward转发到d.jsp页面。
  4. c.jsp页面

d.jsp页面:

  1. 从c.jsp转发到d.jsp
  2. c在请求域中添加键和值,d看能够得到值并且输出
  3. c转发的时候带参数,username和age,在d中获取并且输出
  4. 汉字乱码问题的解决
  5. 转发,url地址栏并没有发生改变
  1. forward作用:在JSP页面上实现转发
  2. param的功能:在转发的时候提供其它的参数名和参数值

我们发现,我们在JSP页面中书写的代码最终都会翻译到_jspService()方法中,我们发现这个方法中有两个形参:HttpServletRequest对象HttpServletResponse对象 。所以说,JSP的本质就是一个Servlet。我们可以直接在JSP页面上使用这两个对象。

改造登录后的页面,用户登陆失败以后,跳转到login.jsp,在jsp页面中动态显示用户登陆错误中的信息。

【思路】

  1. 登录失败后直接转发到一个jsp页面;
  2. 在jsp页面上使用request对象获取request中的值;

【LoginServlet代码】

说明:LoginServlet中,登录失败之后,直接转发到login.jsp页面。

【login.jsp页面】

说明:上述代码中:

能够使用msg获取数据,因为编译之后存在于同一个类中,编译jsp如下所示:

效果图:浏览器访问login.html页面

登录密码输入错误:

小结:

  • JSP作用:给浏览器生成响应信息;
  • JSP特点:动态网页,html+java,由服务器来运行的。本质上是一个Servlet;
  • 书写Java代码的三种方式:
    • 脚本片段:java代码片段,在service方法中起作用。格式:<% System.out.println(\”hello\”) %>;
    • 脚本表达式:直接在页面输出内容,在service方法中起作用,等价于out.print(内容)。格式:<%= \”上海打工摸鱼\”%>
    • 脚本声明:定义成员变量,方法。在当前类中都起作用。<%!int a=10;%>
  • JSP页面常用的对象:request,response;

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。