一、什么是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目录中,如下图
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实例;
如下图所示:
3.1、编译
有三个步骤:
- 解析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就可以了
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的其他内容。