致敬即将退出历史舞台的JSP

时间:2022-03-25 09:25:30

致敬即将退出历史舞台的JSP

        今天小课堂本来是准备讲JSP的隐藏对象的,以前一直以为页面这种东西都是切图仔的事情,JSP的东西我们不用管,关于几个属性的介绍还是比较简单的。赶鸭子上架,临时看了资料,随着查阅的深入,我发现JSP不是我想象的那么简单,在html文件中使用java,表现层实现页面和后台的分离。虽然说JSP技术已经不再是当今的主旋律,但它在web程序设计中所体现的思想和实现技术还是值得我们探讨一下。


一、JSP是什么

       我感觉想要准确的给一个名词下定义是很难的,对于JSP尤为如此,因为他的表现形式和它的本质,大相径庭。想要认识JSP是什么,还是先认识JSP文件在web应用中是怎么执行的,他的作用是什么。

           JSP的执行原理和流程:

 假设:容器=具体的Jsp引擎

Tomcat中的JSP引擎就是一个Servlet程序,它负责解释和执行JSP页面

   1 -----   客户通过浏览器向服务器端的JSP页面发送请求。、


   2 -----  容器接受到客户请求后,会检查JSP文件对应编译后的Servlet文件(.class)是否存在。

            如果不存在,则跳转到第 4 步,否则执行下一步。

 

   3 ----- 容器检查JSP页面是否有更新(修改),没有更新,则跳转到第 5 步,否则执行下一步。

 

   4 ----- 容器将JSP文件转换为Servlet类源文件(.java)。(此步会检查和捕获JSP语法错误


   5 ----- 容器将Servlet源文件(.java)编译成相应的字节码(.class)文件。(会检查和捕获Java语法错误)

 

   6 ----- 容器将字节码(.class)文件加载到内存。

 

   7 ----- 容器实例化Servlet(调用构造函数),然后调用初始化方法(jspInit())初始化Servlet。到此为止,Servlet对象真正成为一个Servlet,准备就绪,可以处理客户的请求了

 

   8 ----- 容器创建一个新的线程来运行Servlet并运行Servlet的_jspService()方法处理客户的请求。

 

   9 ----- Servlet生成并向客户返回一个响应(或把请求转发给另一个Web应用组件处理)。


我们结合一个简单的JSP页面,看看转译的过程

hello  world  

<html>
<head>
   <title>欢迎页</title>
</head>
<body>
<h2>hello world</h2>
</body>
</html>

我们的JSP页面转译的.java和.class 文件会保存在Tomcat的work目录下

致敬即将退出历史舞台的JSP


这是转译后的.java

/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/7.0.88
* Generated at: 2018-06-06 15:32:53 UTC
* Note: The last modified time of this file was set to
*       the last modified time of the source file after
*       generation to assist with modification tracking.
*/
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {

private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();

 private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

 private volatile javax.el.ExpressionFactory _el_expressionfactory;
 private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

 public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
 }

public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
       }
}
}
return _el_expressionfactory;
 }

public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
       }
}
}
return _jsp_instancemanager;
 }

public void _jspInit() {
}

public void _jspDestroy() {
}

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOExceptionjavax.servlet.ServletException {

final javax.servlet.jsp.PageContext pageContext;
   javax.servlet.http.HttpSession session = null;
   final javax.servlet.ServletContext application;
   final javax.servlet.ServletConfig config;
   javax.servlet.jsp.JspWriter out = null;
   final java.lang.Object page = this;
   javax.servlet.jsp.JspWriter _jspx_out = null;
   javax.servlet.jsp.PageContext _jspx_page_context = null;


   try {
response.setContentType("text/html");
     pageContext = _jspxFactory.getPageContext(this, requestresponse,
            null, true, 8192, true);
     _jspx_page_context = pageContext;
     application = pageContext.getServletContext();
     config = pageContext.getServletConfig();
     session = pageContext.getSession();
     out = pageContext.getOut();
     _jspx_out = out;

     out.write("<html>\n");
     out.write("<body>\n");
     out.write("<h2>Hello World!</h2>\n");
     out.write("</body>\n");
     out.write("</html>\n");
   catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
       if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
           else {
out.clearBuffer();
           }
catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
       else throw new ServletException(t);
     }
finally {
_jspxFactory.releasePageContext(_jspx_page_context);
   }
}
}

       我们看到,这个类继承了org.apache.jasper.runtime.HttpJspBase,要想看到这个类的源码,我们需要下载tomcat的源码,然后找到这个类,源码如下:

  1. /* 
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more 
  3.  * contributor license agreements.  See the NOTICE file distributed with 
  4.  * this work for additional information regarding copyright ownership. 
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0 
  6.  * (the "License"); you may not use this file except in compliance with 
  7.  * the License.  You may obtain a copy of the License at 
  8.  *  
  9.  *      http://www.apache.org/licenses/LICENSE-2.0 
  10.  *  
  11.  * Unless required by applicable law or agreed to in writing, software 
  12.  * distributed under the License is distributed on an "AS IS" BASIS, 
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  14.  * See the License for the specific language governing permissions and 
  15.  * limitations under the License. 
  16.  */  
  17.   
  18. package org.apache.jasper.runtime;  
  19.   
  20. import java.io.IOException;  
  21.   
  22. import javax.servlet.ServletConfig;  
  23. import javax.servlet.ServletException;  
  24. import javax.servlet.http.HttpServlet;  
  25. import javax.servlet.http.HttpServletRequest;  
  26. import javax.servlet.http.HttpServletResponse;  
  27. import javax.servlet.jsp.HttpJspPage;  
  28. import javax.servlet.jsp.JspFactory;  
  29.   
  30. import org.apache.jasper.compiler.Localizer;  
  31.   
  32. /** 
  33.  * This is the super class of all JSP-generated servlets. 
  34.  * 
  35.  * @author Anil K. Vijendran 
  36.  */  
  37. public abstract class HttpJspBase   
  38.     extends HttpServlet   
  39.     implements HttpJspPage   
  40.           
  41.       
  42. {  
  43.       
  44.     protected HttpJspBase() {  
  45.     }  
  46.   
  47.     public final void init(ServletConfig config)   
  48.     throws ServletException   
  49.     {  
  50.         super.init(config);  
  51.     jspInit();  
  52.         _jspInit();  
  53.     }  
  54.       
  55.     public String getServletInfo() {  
  56.     return Localizer.getMessage("jsp.engine.info");  
  57.     }  
  58.   
  59.     public final void destroy() {  
  60.     jspDestroy();  
  61.     _jspDestroy();  
  62.     }  
  63.   
  64.     /** 
  65.      * Entry point into service. 
  66.      */  
  67.     public final void service(HttpServletRequest request, HttpServletResponse response)   
  68.     throws ServletException, IOException   
  69.     {  
  70.         _jspService(request, response);  
  71.     }  
  72.       
  73.     public void jspInit() {  
  74.     }  
  75.   
  76.     public void _jspInit() {  
  77.     }  
  78.   
  79.     public void jspDestroy() {  
  80.     }  
  81.   
  82.     protected void _jspDestroy() {  
  83.     }  
  84.   
  85.     public abstract void _jspService(HttpServletRequest request,   
  86.                      HttpServletResponse response)   
  87.     throws ServletException, IOException;  
  88. }  

   好了尘埃落定,JSP就是一个servlet!但这个servlet装修过的,它提供了很多标签,对servlet中的方法,我们只需要定义标签内容即可。

       因为JSP就是servlet,那么生命周期也就是跟serlvet一样。

   JSP和servlet有一点区别就在于:jsp是先部署后编译,而servlet是先编译后部署。我们通常的热部署也是基于此。





 基于对JSP在web容器工程中的转译过程,我们描述一下JSP的定义:

JSP全称是JavaServer Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。
JSP这门技术的最大的特点在于,写jsp就像在写html,但:
它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。
相比servlet而言,servlet很难对数据进行排版,而jsp除了可以用java代码产生动态数据的同时,也很容易对数据进行排版。

不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。

其原因为,程序的数据通常要美化后再输出:
让jsp既用java代码产生动态数据,又做美化会导致页面难以维护。
让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。
因此最好的办法就是根据这两门技术的特点,让它们各自负责各的,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。


二、JSP的组成元素


       接下来我们要看一看,我们的JSP的代码最后servlet类中是扮演什么样的角色,也就是JSP中组成元素最后会放在servlet响应源代码中的什么位置。

----1   JSP模版数据: 就是JSP中的HTML代码,它的内容给是固定的,无论程序如何运行模版数据输出到客户端浏览器时都不会发生改变,当我们创建一个JSP时,模版就已经固定了。


----2   JSP脚本:

         1. 使用<% 编写java代码 %>,中间java代码必须遵循Java语法

         2. 表达式:使用<%=xxx %>来输出结果  ervlet中就会将其转换为out.print(result)进行输出。输出各种类型数据:int、double、boolean、String、Object等

         3.JSP中申明方法与属性(全局变量)  使用<%!  方法、属性%>

         4. 在JSP中使用if语句,或者使用for循环,whilt循环等都可以实现,也就是编写脚本而已


----2   JSP指令:

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

        JSP指令格式:<%@ directive {attribute=value}* %>

           解释:

              directive:指令名称,例如page指令

              attribute=value:紧跟指令名称后面的就是各种属性,以键值对的形式书写

              *:代表后面能跟0个或多个属性。

        例如: page指令:用来声明JSP页面的属性等。

           <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  page指令,后面跟着三个属性,分别是language、contentType、pageEncoding。这只是其中的几个属性,并没有写全。



     前面讲了JSP语法,介绍了JSP页面中的内容有哪些,分别有什么作用,就两个东西,模块数据和元素。其中元素有包括脚本,指令,标签,脚本就是JSP中嵌入java代码,指令作用就是申明页面的属性,


-----4   JSP标签

那标签是干嘛的,标签分为JSP自带内置的标签,和通过taglib指令来使用JSP标签库,或者自定义标签。现在我们先来讲一些JSP内置的标签。

JSP内置的标签就被称为JSP行为(JSP Actions)。只要书写很少的标记代码就能使用JSP提供的丰富功能,JSP行为其实是对常用的JSP功能的抽象与封装,可以取代jsp脚本,让JSP中就少一些嵌入java代码的地方。

      格式:<jsp:elements {attribute="value"}* />  

          jsp:标签的前缀,说明是jsp内置的标签,

          elements:行为的名称,

          attribute=value:使用键值对来编写属性

          *:能指定0个或多个属性对


典型的JSP内置标签认识

<jsp:include />行为

       include行为用于运行时包含某个文件,如果被包含的文件为JSP程序,则先会执行JSP程序,然后在把执行的结果包含进来。 作用是跟include指令一样的,唯一的区别就在于,include指令是将被包含的文件的源码加入到了本JSP程序中,然后在进行编译,属于静态包含,而include行为只是将被包含的文件的运行结果包含进自己。属于动态包含


<jsp:forward />行为

      实现请求转发功能,Servlet中通过request.getRequestDispatcher("someServlet").forward(request,response);而在JSP中也能够实现相同的功能,只不过用的是<jsp:forward />行为,实际上forward行为就是对其进行了封装。

      格式:

        <jsp:forward page="someServlet">

           <jsp:param name="param1" value="value1"/>

           <jsp:param name="param2" value="value2"/>

        </jsp:forward>

        page:需要跳转到的页面或者servlet、 <jsp:param/>参数行为,带一些参数过去,name、value是以键值对的形式带过去的


JSP的自定义标签的产生的目的就是为了替代JSP中的脚本,因为在JSP文件中嵌套代码,不易维护,软件开发的封装思想。

 ----------脚本会慢慢被标签全部代替,也就是说JSP中基本上不会嵌入Java代码--------


------JSP的隐藏对象

        我们知道JSP会转换为servlet,在Servlet中,输出数据时,都需要通过response.getWrite();但是在JSP中,直接使用out对象进行输出,为什么呢?这就是因为out为JSP的一个隐藏对象,JSP中内置了9个隐藏对象,使得JSP比Servlet使用起来更简单,更方便:

 page、config、application、request、response、session、out、exception、pageContext



 page:page对象代表当前JSP页面,是当前JSP编译后的Servlet类的对象。相当于this。

 config:标识Servlet配置,类型:ServletConfig,api跟Servlet中的ServletConfig对象是一样的,能获取该servlet的一些配置信息,能够获取ServletContext

 application:标识web应用上下文,类型:ServletContext,详情就看Servlet中的ServletContext的使用

    request:请求对象,  类型:httpServletRequest

 response:响应对象  类型:httpServletResponse

 session:表示一次会话,在服务器端记录用户状信息的技术

 out:输出响应体 类型:JspWriter

 exception 表示发生异常对象,类型 Throwable,在上面我们介绍page指令中的一个errorPage属性时就有说到他

 pageContext:表示 jsp页面上下文(jsp管理者) 类型:PageContext


    标记了红色的对象就是JSP独有的,其他的都是Servlet中的老东西,只是实现可servlet中的接口。


pageContext对象:重点

  这个功能就比较强大了,比较牛逼,基本上什么他都有,因为是它是JSP页面的管理者(上下文),所以JSP中的内置对象呀,它统统能够获得,下面介绍它的api。

  1、获得其它八大内置对象 getXxx()

  pageContext.getOut();  //获得out对象

  pageContext.getApplication();  //获得application对象

  等等....

  2、对作用域的属性进行操作(四大作用域)

   对默认作用域的属性进行操作。page

   pageContext.getAttribute(name);  //获得page作用域数据

   pageContext.setAttribute(name,value);  //给page作用域设置内容

   pageContext.removeAttribute(name);  //给page作用域移除内容

   3、对指定作用域的属性进行操作

   getAttribute(name,scope);  //获得 指定作用域中的数据

   setAttribute(name,value);  //给指定作用域设置内容

   removeAttribute(name ,scope)  移除指定作用域的内容(page/request/session/application)

   4、提供作用域常量

   PageContext.PAGE_SCOPE  page

      PageContext.REQUEST_SCOPE  request      

   PageContext.SESSION_SCOPE  response

   PageContext.APPLICATION_SCOPE  application

   5、一次获得指定名称内容

   findAttribute(name);  //依次从page、request、session、application 获得内容

 

   response对象:

   就是响应对象,、如果不了解就看看讲解request和response的这一章节的内容

 

   config对象:

   类型:ServletConfig

   能够获取servlet的初始化参数,获取servletContext对象,获取servletName

  

结合这个实例可以帮助你更好地理解

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ page import="com.ppteng.jsp.model.Person " %>
<%
   String path = request.getContextPath();
   String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html>
<head>
   <base href="<%=basePath%>">
   <title>My JSP page</title>
</head>
<body>
<%!
   int count=0;
   public int doubleCount(){
       return count*2;
   }
%>
The page count is now:<%=++count %>
<br>
doubleCount:<%= doubleCount()%>
<br>
<%
   String localVar = "World";
   Person p=new Person();
   p.setName("Hello");
%>
person.name:<%=p.getName() %>
<br>
localVar:<%=localVar %>
<br>
Today is:<%= new Date()%>
</body>
</html>



结语:JSP通过转译servlet的方法,实现页面渲染与事物处理的分离,这种定义模板、标签来实现对抽象类型的简化,在我们的程序中随处可见,JSP的编译内容是我们可以理解java语言,整个过程显得形象一点,容易理解。


JSP技术可以说是很有创新性的;

使得我们不用为输出html标签而发愁

同时作为Java平台的一部分,JSP拥有Java编程语言“一次编写,各处运行”的特点。JSP页面也具有Java技术的所有好处,包括健壮的存储管理和安全性。

SP标签可扩充性 ,允许开发者扩展JSP标签,定制JSP标签库,所以网页制作者充分利用与XML兼容的标签技术强大的功能,大大减少对脚本语言的依赖.由于定制标签技术,使网页制作者降低了制作网页的复杂度. 

但随着现今项目的体量的增大,JSP的缺点被放大;对于各种各样的需求也显得力不从心:

    1.增大了服务器的压力
    2.前端与后端未脱离,拖慢开发进度
    3.过于依赖java运行环境

    4.复用较低。