Servlet 与 JSP 核心编程

时间:2020-12-24 21:03:07

阅读书目,清华大学出版社 《Servlet 与 JSP 核心编程》第二版Marty Hall Larry Brown著
记录读书体会,笔记内容掺杂个人理解,必定会有大量不妥甚至不对的地方,随着知识的积累会不断修正错误之处


1 原书第 7 页,知识点

JSP 文档不过是编写 Servlet 的另一种方式,JSP 页面会被翻译成 servlet,servlet 会被编译,在请求期间运行的就是 servlet。


2 原书第 48 页,知识点

servlet 是运行在 Web 服务器或应用服务器上的程序,它担当 Web 浏览器或者其他 HTTP 客户程序与 HTTP 服务器上的数据库或应用程序之间的中间层。

3 积累
Servlet 与 JSP 核心编程

以 Servlet 为例

1 servlet 规范由 JCP(Java Community Process) 制定的,提供 api 接口,但不一定提供接口 api 的实现源码,这里所说的“实现”指的是 相关接口 api 的源代码,例如 javax.servlet, javax.servlet.http 等包下面类(Servlet、ServletConfig、HttpServletRequest类等)的源代码,因为编译时需要这些类。

2 以 Tomcat 为例,tomcat 提供的源代码包中包括了所有 JCP 组织Servlet规范 api 中接口、类的实现。

3 除实现 servlet api 中定义的接口、类之外,tomcat 还需要完成具体接口的实现,例如 api 中定义的 Interface HttpServletRequest 接口,tomcat 需要实现此接口所有的方法。


3 原书第 220 页,知识点
原书内容

JSP 页面需要转换成 Servlet,Servlet 在编译后,载入到服务器的内容中,初始化并执行。但是每一步发生在什么时候?要回答这个问题,要牢记以下两点:
1 JSP 页面仅在修改后第一次访问时,才会被转换成 Servlet 并进行编译
2 载入到内存中,初始化和执行遵循 Servlet 一般的规则
Servlet 与 JSP 核心编程


4 原书第 240 页,知识点
原书内容

使用声明
使用 JSP 声明可以将方法或者字段的定义插入到 servlet 类的注定一体中(位于对请求进行处理的 _jspService 方法之外)。声明形式如下:
<%! Filed or Method Definition %>
由于声明并不产生输出,所以,它们一般与 JSP 或者 scriptlet 结合使用。基本上,JSP 声明可以包含字段(实例变量)定义、方法定义、内部类定义、甚至是静态初始块。任何可以被方法在类中定义中但在已有方法之外的内容。然而在实践中,声明几乎总是包含字段或方法定义。
但要注意,不要是使用 JSP 声明覆盖 servlet 的标准生命周期方法(service、detory、init 方法)。由 JSP 转换成的 servlet 已经使用这些方法。由于对 service 方法的调用会自动分派给 _jspService(也就是表达式和 scriptlet 生成的代码防止的地方),因而声明不需要访问 service 方法。但对于初始化和清理工作,可以覆盖 jspInit 和 jspDestory 方法(在有 JSP 转换而生成的 servlet 中,标准的 init 和 destory 方法会保证调用这两个方法)。
除了覆盖 jspInit 和 jspDestory 方法,JSP 声明在定义方法方面的功用是值得质疑的。请将这些方法移到单独的类中,这样更有利于他们的编写(这是由于你可以使用 java 开发环境进行编写工作,而不是在类 HTML 环境中进行)、测试(不需要运行服务器)、调试(编译警告能够给出正确的行号;查看表转输出不需要使用其他技巧)和重用(许多不同的 JSP 页面可以使用同一个实用工具类)。但是,很快我们会看到,应用 JSP 声明定义实例变量(字段)可以赋予您一些应用单独的使用工具类不已完成的功能,它为跨请求持续性数据的存储提供一席之地。

我的体会

1 JSP 声明中不可以声明 service、init 和 destory 方法,更具体的说应该是不能重写上述生命周期方法。接下来看一下为什么,下面是 index.jsp 转化成 java 类的样子。

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

public abstract class HttpJspBase
extends HttpServlet
implements HttpJspPage

index_jsp 继承自 HttpJspBase,HttpJspBase 继承自 HttpServlet ,毫无疑问 final class index_jsp 是一个 servlet

public final void init(ServletConfig config) 
throws ServletException {
super.init(config);
jspInit();
_jspInit();
}
public final void destroy() {
jspDestroy();
_jspDestroy();
}
public final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
_jspService(request, response);
}

上面三个方法是在 HttpJspBase 类中的定义,而且全部定义为 final 类型,所以子类 index_jsp 中无法重写这些方法。
index_jsp 中有两组下面的方法

public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
}

public void jspInit() {
}

public void _jspDestroy() {
}

public void jspDestroy() {
}

1 JSP 声明中无法重写 service、init 和 destory 方法,因为其父类实现类实现了上述方法,并将其定义为 final类型。
2 如果 JSP 中需要初始化操作和类卸载时做出相应的响应,那么需要通过声明的方式重写 jspInit()和 jspDestroy()方法,而不是 _jspInit()和_jspDestroy()方法。
3 JSP 跟 servlet 没有任何差别,也是单例模式,所以通过声明的方式定义的成员变量可以实现跨请求共享访问,但是需要自己提供同步机制。


5 原书第 259页,知识点
原书内容
isThreadSafe 属性控制有 JSP 页面生成的 servlet 是否允许并行访问(默认)。可以此采用以下两种形式

<%@ page isThreadSafe="true" %> <%-- default --%>
表示该 JSP 页面是线程安全的,不需要并发控制
<%@ page isThreadSafe="false" %>
表示该 JSP 页面不是线程安全的,需要并发控制

1 Jsp 页面 page 指令中的 isThreadSafe属性值设置为 false 时,生成的 servlet 类会实现 SingleThreadModel 接口

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

2 Servlet 和 Jsp 规范已经废弃了这种并发控制安全的做法,主要原因是 SingleThreadModel 导致 jsp 页面被请求线程顺序访问,高流量的 JSP 页面可能会很慢。


6 原书第 262 页,知识点

  • jsp:include
  • include 指令
  • jsp:forward

jsp:include 动作:在主页面被请求时,将次级页面的输出包含进来,发生在页面请求期间。
可以将任何内容插入到 JSP 的输出中

  • HTML 页面的内容
  • 纯文本文档的内容
  • JSP 页面的输出
  • servlet 的输出

include.jsp 源代码

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
1 <jsp:include page="/WEB-INF/included_page.jsp" flush="false" />
<br>
2 <jsp:include page="/WEB-INF/included_page.txt" flush="false" />
<br>
3 <jsp:include page="/WEB-INF/included_page.html" flush="false" />
<br>
4 <jsp:include page="/included_page_Servlet" flush="true" />
</body>
</html>

请求 include.jsp 时,include.jsp 转换成 servlet 的部分源代码

out.write(" \r\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("\t<body>\r\n");
out.write("\t\t1 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/WEB-INF/included_page.jsp", out, false);
out.write("\r\n");
out.write("\t\t<br>\r\n");
out.write("\t\t2 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/WEB-INF/included_page.txt", out, false);
out.write("\r\n");
out.write("\t\t<br>\r\n");
out.write("\t\t3 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/WEB-INF/included_page.html", out, false);
out.write("\r\n");
out.write("\t\t<br>\r\n");
out.write("\t\t4 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/included_page_Servlet", out, true);
out.write("\r\n");
out.write("\t</body>\r\n");
out.write("</html>");