Javaweb之jsp详解一

一、什么是JSP

JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。标签通常以<%开头以%>结束。

JSP的出现,是替代最初的Servlet,与纯 Servlets相比:JSP可以很方便的编写或者修改HTML网页而不用去面对大量的println语句。JSP页面可以与处理业务逻辑的servlets一起使用,这种模式被Java servlet 模板引擎所支持。

为什么JSP也可以对数据和请求进行进行处理,因为JSP的本质就是一个Servlet,由于两者的差异性导致一般让servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,jsp来显示数据。

二、JSP与Servlet的关系

JSP的本质就是一个Servlet,服务器会将一个JSP文件翻译为一个java文件,如下serializa.jsp文件

<%@page import="listen.bean.SerializListener"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">

    <title>HttpSessionActivationListener监听器</title>

    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>

  <body>
    <%
     session.setAttribute("bean",new SerializListener("影梧"));   
    %>
  </body>
</html>

服务器会将其翻译的文件放在Tomcat中的work目录中,如下图
《Javaweb之jsp详解一》
serializa_jsp.java文件的内容如下:


/* * Generated by the Jasper component of Apache Tomcat * Version: Apache Tomcat/9.0.10 * Generated at: 2018-12-06 06:37:29 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.*; import listen.bean.SerializListener; import java.util.*; public final class serializa_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent, org.apache.jasper.runtime.JspSourceImports { 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 static final java.util.Set<java.lang.String> _jspx_imports_packages; private static final java.util.Set<java.lang.String> _jspx_imports_classes; static { _jspx_imports_packages = new java.util.HashSet<>(); _jspx_imports_packages.add("javax.servlet"); _jspx_imports_packages.add("java.util"); _jspx_imports_packages.add("javax.servlet.http"); _jspx_imports_packages.add("javax.servlet.jsp"); _jspx_imports_classes = new java.util.HashSet<>(); _jspx_imports_classes.add("listen.bean.SerializListener"); } 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 java.util.Set<java.lang.String> getPackageImports() { return _jspx_imports_packages; } public java.util.Set<java.lang.String> getClassImports() { return _jspx_imports_classes; } 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 { if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) { final java.lang.String _jspx_method = request.getMethod(); if ("OPTIONS".equals(_jspx_method)) { response.setHeader("Allow","GET, HEAD, POST, OPTIONS"); return; } if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) { response.setHeader("Allow","GET, HEAD, POST, OPTIONS"); response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET, POST or HEAD. Jasper also permits OPTIONS"); return; } } 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;charset=UTF-8"); 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("\r\n"); out.write("\r\n"); String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; out.write("\r\n"); out.write("\r\n"); out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n"); out.write("<html>\r\n"); out.write(" <head>\r\n"); out.write(" <base href=\""); out.print(basePath); out.write("\">\r\n"); out.write(" \r\n"); out.write(" <title>HttpSessionActivationListener监听器</title>\r\n"); out.write(" \r\n"); out.write("\t<meta http-equiv=\"pragma\" content=\"no-cache\">\r\n"); out.write("\t<meta http-equiv=\"cache-control\" content=\"no-cache\">\r\n"); out.write("\t<meta http-equiv=\"expires\" content=\"0\"> \r\n"); out.write("\t<meta http-equiv=\"keywords\" content=\"keyword1,keyword2,keyword3\">\r\n"); out.write("\t<meta http-equiv=\"description\" content=\"This is my page\">\r\n"); out.write("\t<!--\r\n"); out.write("\t<link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\">\r\n"); out.write("\t-->\r\n"); out.write("\r\n"); out.write(" </head>\r\n"); out.write(" \r\n"); out.write(" <body>\r\n"); out.write(" "); session.setAttribute("bean",new SerializListener("影梧")); out.write("\r\n"); out.write(" </body>\r\n"); out.write("</html>\r\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); } } }

可以看到
将jsp的HTML代码以out.println的形式输出,这个类继承了org.apache.jasper.runtime.HttpJspBase

查看源码发现其继承了HTTPServlet,所以JSP的本质就是Servlet

public abstract class HttpJspBase  extends HttpServlet  implements HttpJspPage   

三、JSP的生命周期

JSP的生命周期包括以下几个阶段:

  • 编译阶段:servlet容器编译servlet源文件,生成servlet类;
  • 初始化阶段:加载与JSP对应的servlet类,创建其实例,并调用它的初始化方法;
  • 执行阶段:调用与JSP对应的servlet实例的服务方法;
  • 销毁阶段:调用与JSP对应的servlet实例的销毁方法,然后销毁servlet实例;

如下图所示:
《Javaweb之jsp详解一》

3.1、编译

《Javaweb之jsp详解一》
有三个步骤:

  • 解析JSP文件;
  • 将JSP文件转化为servlet;
  • 编译servlet;

这里,首先判断是不是第一次请求,如果是的话,也就是说JSP还没有被编译过,JSP引擎就把相应的JSP文件编译成servlet,生成字节码文件,并调用jspInit();如果不是第一次请求,说明已经有了字节码文件,那么就开始解析执行,调用jspServive()。

jspService()方法被调用来处理客户端的请求,对于每一个请求,JSP引擎将创建一个新的线程来处理请求。如果有多个客户端同时请求JSP文件,则JSP引擎将会创建多个线程,每个客户端对应一个线程。同时,servlet始终存在内存中,因此相应很快。

3.2、初始化

容器载入JSP文件后,它会在为请求提供任何服务前调用jspInit()方法。如果需要执行自定义的JSP初始化任务,复写jspInit()方法就行了,就像下面这样:

    public void jspInit() {
        //init code
    }

通常,程序只初始化一次。

3.3、JSP执行

这一阶段描述了JSP生命周期中一切与请求相关的交互行为,直到被销毁。
当JSP页面完成初始化后,调用jspService()方法执行。

3.4、JSP清理

JSP生命周期的销毁阶段描述了当一个JSP网页从容器中被移除时所发生的一切。
jspDestroy()方法在JSP中等价于servlet中的销毁方法。当您需要执行任何清理工作时复写jspDestroy()方法,比如释放数据库连接或者关闭文件夹等等。

四、jsp的语法

1.JSP表达式
2.JSP脚本片段
3.JSP声明
3.JSP注释
4.JSP指令
5.JSP标签
6.JSP内置对象

4.1 JSP脚本表达式

JSP脚本表达式(expression)用于将程序数据输出到客户端
语法:<%= 变量或表达式 %>或者

<jsp:expression>
   表达式
</jsp:expression>

举例:当前时间:<%= (new java.util.Date()).toLocaleString() %>
JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。
JSP脚本表达式中的变量或表达式后面不能有分号(;)。

Jsp翻译之后的Servlet中的脚本表达式翻译的结果:

out.print(new Date().toLocalString());

4.2 JSP的脚本片段

JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。语法:
<% 多行java代码 %>
或者

<jsp:scriptlet>
   代码片段
</jsp:scriptlet>

注意:JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。

在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。
多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。如:out.println(x);
单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句

<% 
    for (int i=1; i<5; i++)  
    { 
%> 
    <H1>www.it315.org</H1> 
<% 
    } 
%>   

4.3 JSP声明

一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。在JSP文件中,您必须先声明这些变量和方法然后才能使用它们。

SP页面中编写的所有代码,默认会翻译到servlet的service方法中, 而Jsp声明中的java代码被翻译到_jspService方法的外面。语法:
<%! java代码 %>
或者

<jsp:declaration>
   代码片段
</jsp:declaration>

4.4 JSP注释

Jsp注释

JSP注释的格式:
<%-- 注释信息 --%>
JSP引擎在将JSP页面翻译成Servlet程序时,忽略JSP页面中被注释的内容。

语法 描述
<%-- 注释 --%> JSP注释,注释内容不会被发送至浏览器甚至不会被编译
<!-- 注释 --> HTML注释,通过浏览器查看网页源代码时可以看见注释内容
<\% 代表静态 <%常量
%> 代表静态 %> 常量
\’ 在属性中使用的单引号
\” 在属性中使用的双引号

4.5 JSP指令

JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。在JSP2.0规范中共定义了三个指令:

  • page指令
  • Include指令
  • taglib指令

JSP指令的基本语法格式:
<%@ 指令 属性名="值" %>

4.5.1 page指令

page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。
JSP 2.0规范中定义的page指令的完整语法:

<%@ page [ language="java" ] [ extends="package.class" ] [ import="{package.class | package.*}, ..." ] [ session="true | false" ] [ buffer="none | 8kb | sizekb" ] [ autoFlush="true | false" ] [ isThreadSafe="true | false" ] [ info="text" ] [ errorPage="relative_url" ] [ isErrorPage="true | false" ] [ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ] [ pageEncoding="characterSet | ISO-8859-1" ] [ isELIgnored="true | false" ] %>

4.5.1.1、language属性

指定JSP Container用什么语言来编译,目前只支持JAVA语言。默认为JAVA
language属性不重要,是jsp设计出来想支持其他语言的,但是。。。。(我太高估自己的—-JSP)。。。。。。

4.5.1.2、extends属性

定义此JSP网页产生的Servlet是继承哪个,一般用不到此属性

4.5.1.3、important属性

定义此JSP网页要使用哪些Java API

JSP 引擎自动导入下面的包:
java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*

可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号分隔:
<%@ page import="java.util.Date,java.sql.*,java.io.*"%>

4.5.1.4、session属性

决定此页面是否使用session对象。默认为true

就是在将Jsp翻译成Servlet的时候不会传递Session对象,当属性值设置为false时,以下代码不会出现在翻译后的Servlet源码中:

 javax.servlet.http.HttpSession session = null;
4.5.1.5、buffer属性

决定输出流(Input stream)是否又缓冲区。默认为8kb,如果想关闭缓冲区的话,只需要设置值为none就可以了

《Javaweb之jsp详解一》

4.5.1.6、autoFlush

决定输出流的缓冲区满了后是否需要自动清除,缓冲区满了后会产生异常错误(Exception).默认为true

4.5.1.7、isThreadSafe

是否支持线程。默认为true

这个属性见名知意,是设置是否线程安全的,我们在之前讨论Servlet的时候,说到了Servlet是个单例对象,是线程不安全的,我们那时候可以通过实现一个接口来实现线程安全,这里只需要设置这个属性值就可以控制Jsp翻译之后的Servlet时候线程安全:
<%@ page isThreadSafe="true|false" %> 默认值为true

为什么servlet线程不安全?
servlet是单例的,每个用户访问都会生成一个线程为其处理。然而servlet的是单例的。也就说一个servlet只能生成一个对象,这样多个线程操作一个实例对象,必然可能造成线程不安全。比如一个成员变量,其中一个线程改变他,还没有退出对该线程的访问。另外一个线程也进来了,也改变了这个成员变量,那么第一个线程返回的值很有可能不是自己修改的,而是第二个线程修改的值。

isThreadSafe=false模式表示它是以Singleton模式运行。
该模式implements了接口SingleThreadMode,
该模式同一时刻只有一个实例,不会出现信息同步与否的概念。
若多个用户同时访问一个这种模式的页面,
那么先访问者完全执行完该页面后,后访问者才开始执行。
isThreadSafe=true模式表示它以多线程方式运行。
该模式的信息同步,需访问同步方法(用synchronized标记的)来实现。

我们将值设置成false之后发现翻译Jsp之后的Servlet接口SingleThreadMode

4.5.1.8、info属性

指定页面信息,一般用不到

4.5.1.9、errorPage属性

如果此页发生异常,网页会重新指向一个url
例如:

<%@ page pageEncoding="utf-8" errorPage="/erro.jsp" info="这是info信息的测试"  contentType="text/html;charset=utf-8"%>

<html>
  <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
  <title>用户表单</title>
  </head>
 <body>
 <% 
    int x = 1/0; 
%>  
 </body>
 </html>

访问以上jsp会转发到erro.jsp页面,浏览器地址栏不会发生变化,因为应用了转发技术

4.5.1.10、isErrorPage属性

<@ page isErrorPages=”false|true”%> 默认值是false,用来表明该jsp是一个erro页

4.5.1.11、contentType属性和pageEncoding属性
  • contentType属性表示MIME类型和JSP的编码方式。
    一般为
contentType=”text/html;charset=gb2312”
  • pageEncoding属性表示该JSP页面的编码方式

<%@ page contentType="text/html;charset=GBK" %> 页面用GBK编码
如果有pageEncoding这一项,则采取这一项的值,如果没有,采取charset的值,如果都没有,采取iso8859-1。

4.5.1.12、isELIgnored属性

表示是否在此JSP页面中EL表达式。true则忽略,反之false则支持。默认为false

4.5.2 include指令

include指令很简单,就是实现页面包含的,request实现的包含是动态包含,而使用include指令来实现页面包含是静态包含,关于静态包含和动态包含,我们在下面介绍jsp:include标签的时候在详细说明

request.getRequestDispatcher("资源路径").include(request, response);
request.getRequestDispatcher("资源路径").forward(request, response);

<%@include file="/404.jsp" %>静态包含,404.jsp文件的所有内容都包含到该指令所在的位置,如果在两个JSP页面定义了相同的局部变量路径会提示错误Duplicate local variable path

静态包含也就是两个jsp页面合并成一个Servlet,动态包含就是两个Servlet,需要时合并在一起

4.5.3 taglib指令

这个指令作用也是很简单的,就是引入标签,这个之后再我们后面学习JSTL的内容的时候在作介绍

闲言碎语

本章较为详细的介绍了什么是JSP以及JSP一些内容,篇幅有限,下一篇继续研究JSP的其他内容。

参考

JavaWeb学习篇之—-Jsp详解
JSP的结构和生命周期

菜鸟教程jsp

点赞

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注