致敬即将退出历史舞台的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目录下
这是转译后的.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.IOException, javax.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, request, response,
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的源码,然后找到这个类,源码如下:
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.jasper.runtime;
- import java.io.IOException;
- import javax.servlet.ServletConfig;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.jsp.HttpJspPage;
- import javax.servlet.jsp.JspFactory;
- import org.apache.jasper.compiler.Localizer;
- /**
- * This is the super class of all JSP-generated servlets.
- *
- * @author Anil K. Vijendran
- */
- public abstract class HttpJspBase
- extends HttpServlet
- implements HttpJspPage
- {
- protected HttpJspBase() {
- }
- public final void init(ServletConfig config)
- throws ServletException
- {
- super.init(config);
- jspInit();
- _jspInit();
- }
- public String getServletInfo() {
- return Localizer.getMessage("jsp.engine.info");
- }
- public final void destroy() {
- jspDestroy();
- _jspDestroy();
- }
- /**
- * Entry point into service.
- */
- public final void service(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException
- {
- _jspService(request, response);
- }
- public void jspInit() {
- }
- public void _jspInit() {
- }
- public void jspDestroy() {
- }
- protected void _jspDestroy() {
- }
- public abstract void _jspService(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException;
- }
好了尘埃落定,JSP就是一个servlet!但这个servlet装修过的,它提供了很多标签,对servlet中的方法,我们只需要定义标签内容即可。
因为JSP就是servlet,那么生命周期也就是跟serlvet一样。
JSP和servlet有一点区别就在于:jsp是先部署后编译,而servlet是先编译后部署。我们通常的热部署也是基于此。
基于对JSP在web容器工程中的转译过程,我们描述一下JSP的定义:
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的缺点被放大;对于各种各样的需求也显得力不从心:
4.复用较低。