Tomcat 与 Java Web开发技术详解(第三版)第六章 JSP技术 笔记
作者:互联网
文章目录
- 第六章 JSP技术
第六章 JSP技术
6.1 比较 HTML、Servlet 和 JSP
6.1.1 静态 HTML 文件
对于hello.html文件,存在于Web应用的文件系统中,当客户端请求访问时,Web服务器会读取文件系统中的hello.html文件,把它作文响应正文发送给浏览器。所以,每次客户访问该文件,客户得到的时同样的内容。
6.1.2 用Servelt动态生成 HTML 文件
public class HelloServlet extends HttpServlet {
/** 响应客户请求*/
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
//获得username请求参数
String username=request.getParameter("username");
/*输出HTML文档*/
PrintWriter out = response.getWriter();
out.println("<html><head><title>helloApp</TITLE></head>");
out.println("<body>");
out.println("<b>Hello,"+username+"</b>");
out.println("</body></html>");
out.close(); //关闭PrintWriter
}
}
6.1.3 用 JSP 动态生成 HTML 文档
在传统的HTML文件中加入java程序片段和JSP标记,就构成了JSP文件。
hello.jsp
<html>
<head>
<title>helloapp</title>
</head>
<body>
<b>Hello,<%= request.getParameter("username") %></b>
</body>
</html>
<%= request.getParameter(“username”) %> 采用了JSP语法,输出request.getParameter(“username”) 的返回值。
当浏览器访问hello.jsp时,按照以下流程来处理客户请求:
- 查找与 JSP 文件对应的Servlet,如果存在,就调用它的服务方法。
- 如果与JSP对应额Servlet不存在,就解析文件系统中的JSP文件,把它翻译成Servlet源文件,接着把Servlet源文件编译为 Servlet类,然后再初始化并运行Servlet。
Tomcat把由JSP生成的Servlet源文件和类放于<CATALINA_HONME>/work目录下。
hello.jsp.java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.jsp;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.el.ExpressionFactory;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspFactory;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.SkipPageException;
import org.apache.jasper.runtime.HttpJspBase;
import org.apache.jasper.runtime.InstanceManagerFactory;
import org.apache.jasper.runtime.JspSourceDependent;
import org.apache.jasper.runtime.JspSourceImports;
import org.apache.tomcat.InstanceManager;
public final class hello_jsp extends HttpJspBase implements JspSourceDependent, JspSourceImports {
private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
private static Map<String, Long> _jspx_dependants;
private static final Set<String> _jspx_imports_packages = new HashSet();
private static final Set<String> _jspx_imports_classes;
private volatile ExpressionFactory _el_expressionfactory;
private volatile InstanceManager _jsp_instancemanager;
static {
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
public hello_jsp() {
}
public Map<String, Long> getDependants() {
return _jspx_dependants;
}
public Set<String> getPackageImports() {
return _jspx_imports_packages;
}
public Set<String> getClassImports() {
return _jspx_imports_classes;
}
public ExpressionFactory _jsp_getExpressionFactory() {
if (this._el_expressionfactory == null) {
synchronized(this) {
if (this._el_expressionfactory == null) {
this._el_expressionfactory = _jspxFactory.getJspApplicationContext(this.getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return this._el_expressionfactory;
}
public InstanceManager _jsp_getInstanceManager() {
if (this._jsp_instancemanager == null) {
synchronized(this) {
if (this._jsp_instancemanager == null) {
this._jsp_instancemanager = InstanceManagerFactory.getInstanceManager(this.getServletConfig());
}
}
}
return this._jsp_instancemanager;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (!DispatcherType.ERROR.equals(request.getDispatcherType())) {
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(405, "JSPs only permit GET, POST or HEAD. Jasper also permits OPTIONS");
return;
}
}
JspWriter out = null;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
response.setContentType("text/html");
PageContext pageContext = _jspxFactory.getPageContext(this, request, response, (String)null, true, 8192, true);
_jspx_page_context = pageContext;
pageContext.getServletContext();
pageContext.getServletConfig();
pageContext.getSession();
out = pageContext.getOut();
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>helloapp</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" <b>Hello,");
out.print(request.getParameter("username"));
out.write("</b>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
} catch (Throwable var13) {
if (!(var13 instanceof SkipPageException)) {
out = (JspWriter)_jspx_out;
if (_jspx_out != null && ((JspWriter)_jspx_out).getBufferSize() != 0) {
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (IOException var12) {
}
}
if (_jspx_page_context == null) {
throw new ServletException(var13);
}
_jspx_page_context.handlePageException(var13);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
从以上代码可以看出,与JSP对应的Servlet类org.apache.jasper.runtime.HttpJspBase类,HttpJspBase类由Tocmat提供,HttpJspBase类实现了JSP API 中的HttpJspPage接口,该接口继承了javax.servlet.jsp.JspPage接口,而JspPage接口继承了Servlet API中的javax.servlet.Servlet接口。
hello.jsp中的HTML 文本称为模板文本,它会被原封不动的发送个客户端。以下代码用于输出模板文件。
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>helloapp</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" <b>Hello,");
out.print(request.getParameter("username"));
out.write("</b>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.print(request.getParameter(“username”)); 与jsp文件中的<%=request.getParameter(“username”)%> 对应。
从上述可以看出,JSP形式上是HTML文件,但是本质上是Servlet。所以Servlet的特性都适用于JSP。
6.2 JSP语法
6.2.1 JSP指令(Directive)
JSP指令(<%@ 和%>内)用来设置和整个JSP网页相关的属性,如网页的编码方式和脚本语言等。
<%@ 指令名 属性="值"%>
常用的三种指令为page、include和taglib。
1. page 指令
page指令可以指定所使用的编程语言、与JSP对应的Servlet所实现的接口、所扩展的类以及导入的软件包等。语法形式如下:
<%@ page 属性1="值1" 属性2="值2" %>
page指令的属性表
page指令的属性 | 描述 | 举例 |
---|---|---|
language | 指定程序代码所使用的编程语言,目前仅java为有效值和默认值 | <%@ page lanuage=“java”%> |
method | 指定java程序片段(SDcriptlet)所属的方法名称java程序片段会成为指定方法的主体。默认方法时serivce(),当多次使用该指令时只有第一次是有效的。有效值包括service,doGet,doPost等 | <%@ page method=“doPost”%> |
import | 导入指定的java软件包,可以使用多次,可以用逗哈分隔 | <%@ page import=“java.io.*,java.util.Hshtable”%> |
content_type | 指定响应结果的MIME类型,默认是text/html,默认编码是ISO-8859-1 | <%@ page content_type=“text/html;chaset=GBK”%> |
session=“true/false” | 指定jsp是否使用session,默认true | <%@ page session=“true” |
errorPage=“error_url” | 指定当发生异常时,客户请求转发到哪个网页 | <%@ page errorPage=“error.jsp”%> |
2. include指令
JSP通过include指令来包含其他文件的内容,被包含的文件可以是JSP或HTML文件。语法为:
<%@ include file="目标组件的绝对URL或相对URL" %>
把多数网页都需要的相同的内容放在一个文件中,其他的JSP文件通过 include 指令来将这个文件包含进来,提高代码的可重用性。
banner.jsp
<body>
<img src="logo.bmp">
</body>
bookstore.jsp
<@ page content_type="text/html;charset=GBK"%>
<@ include file="common.jsp"%>
<html>
<head>
<title>helloapp</title>
</head>
<@ include file="banner.jsp"%>
<body>
<b>Hello</b>
</body>
</html>
用 include 指令来包含其他文件被看做时静态包含。
6.2.2 JSP 声明
JSP声明(<%! 和 %>内)用于声明JSP对应的Servlet类的成员变量和方法。语法如下
<%! declaration;[frvilststion;]....%>
例如:
<%! int v1=0;%>
<%! int v2,v3,v4;%>
<%! String v5="hello"; static int v6;%>
<%!
public String amethod(int i){
if (i < 3){
return "i<3";
} else {
return "i>=3";
}
}
%>
6.2.3 java 程序片段(Scriptlet)
在JSP文件中,可以在 "<% 和 %>"标记间直接嵌入任何有效的java程序代码。这种嵌入的程序片段成为Scriptlet。如果在page指令中没有指定method属性,那么这些代码默认是属于JSP对应的Servlet类的service()方法中的代码块。
<%
String gender="female";//局部变量
if(gender.equals("female")) {
%>
she is a girl. <%-- 模板文本 --%>
<% } else { %>
He is a boy
<% } %>
等价于Servlet service()方法中的:
String gender = "female";
if (gender.equals("female")) {
out.print("she is a girl");
} else {
out.print("He is a boy");
}
6.2.4 Java 表达式
java表达式的标记为 <%= %> ,如果在JSP文件的模板文本中使用该标记,那么它能把表达式的值输出到网页上。
6.2.5 隐含对象
JSP中的隐含对象
隐含对象的引用变量 | 隐含对象的类型 |
---|---|
request | javax.servelt.HttpServletRequest |
response | javax.servlet.HttpServletResponse |
pageContext | javax.servlet.jsp.PageContext |
application | java.servlet.ServletContext |
out | java.servlet.jsp.JspWriter |
config | java.servlet.ServletConfig |
page | java.lang.Object(相当于Java中的this关键字) |
session | javax.servlet.http.HttpSession |
exception | java.lang.Exception |
6.3 JSP 的生命周期
JSP 与 Servlet的区别在于,Servlet容器必须先把JSP编译成 Servlt 类,然后才允许它。
- 解析阶段:Servlet容器解析JSP文件的代码,如果有语法错误,就会想客户端返回错误信息。
- 翻译阶段:Servlet容器将 JSP 文件翻译成 Servlet 源文件。
- 编译阶段:Servlet 容器编译 Servlet源文件,生成 Servlet类。
- 初始化阶段:加载与 JSP 文件对应的 Servlet类,创建实例。
- 运行阶段:调用Servlet实例的方法。
- 销毁阶段:调用与 JSP 对应的 Servlet的销毁方法。
解析、翻译、编译阶段是JSP生命周期中独有的,这三个阶段发生在一下场合:
- JSP 文件首次被访问。
- JSP 文件被更新。
- JSP 文件对应的 Servelt 类被手动删除。
JSP 对应的Servlet类实现了 javax.servlet.jsp.JspPage接口,JspPage继承了javax.servlet.Servlet接口。JspPage接口中定义了jspInit()方法和jspDestroy()方法,与Servlet中的init()和destory对应。在编写JSP文件时,可以实现jspInit()方法和jspDestory()方法。
<%!
public void jspInit(){
//初始换操作
}
%>
<%!
public void jspDestory(){
//销毁操作
}
%>
visit.jsp
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.io.*" %>
<html><head><title>visit.jsp</title></head><body>
<%!
File tempDir=null; //实例变量
public void jspInit(){
ServletContext application=getServletConfig().getServletContext();
tempDir=(File)application.getAttribute("javax.servlet.context.tempdir");
}
%>
工作目录为:<%=tempDir.getPath() %>
</body></html>
对于visit.jsp,Servlet容器在编译时,会产生错误编译,因为application变量是在JSP的_jspService()方法中定义的局部变量,在其他任何地方都是无法访问的。可以通过getServletConfig().getServletContext()方法来获取ServletContxt对象。
6.4 请求转发
JSP采用 <jsp:forward>
标签来进行请求转发。
<jsp:forwrd page="转发的目标组件的绝对地址或相对地址">
JSP源组件的<jsp:forward>
标签以后的代码不会被执行。
<jsp:param>
标签来向转发的目标组件传递额外的请求参数。
<html>
<head><title>source組件</title></head>
<body>
<jsp:forwrad page="target.jsp">
<jsp:param name="username" value="Tom" />
<jsp:param name="password" value="123456" />
</jsp:forward>
</body>
</html>
在target.jsp中可以通过request.getparameter(“username”)来获取参数。
6.5 包含
用include指令来包含,静态包含
<%@ include file="被包含组件的绝对路径或相对路径"%>
用include标签包含,动态包含
<jsp:include page="被包含组件的绝对路径或相对路径">
6.5.1 静态包含
sin.jsp
sin.jsp is including content.jsp.
<% int var=1;
request.setAttribute("username","Tom");
%>
<%@ include file="content.jsp" %>
<p>sin.jsp is doing something else.
content.jsp
<p>
Output from content.jsp:
<br>
var=<%=var %>
<br>
username=<%=request.getAttribute("username") %>
当请求访问sin.jsp时,Tomcat按以下流程响应客户端:
- 解析sin.jsp,在解析 <%@ include file=“content.jsp” %> 时,把content.jsp的所有资源融合到sin.jsp中。融合后的代码如下:
sin.jsp is including content.jsp.
<% int var=1;
request.setAttribute("username","Tom");
%>
<%--content.jsp开始 --%>
<p>
Output from content.jsp:
<br>
var=<%=var %>
<br>
username=<%=request.getAttribute("username") %>
<%--content.jsp结束 --%>
<p>sin.jsp is doing something else.
- 把融合后的源码翻译为Servlet源文件,再编译为Servlet类。
- 初始化与sin.jsp对应的Servlet,再运行它的服务方法。
得出,静态包含是发生在解析JSP源组件阶段,被包含的目标文件中的内容被原封不动的添加到了JSP源组件中,Servlet容器再对JSP源组件进行翻译和编译。静态包含可以为HTML或JSP文件,单不能为Servlet。如果为JSP文件,那么被包含文件可以访问源组件中定义的局部变量,因为实际上,JSP 源组件和被包含组件对应的是同一个Servlet。
6.5.2 动态包含
din.jsp
din.jsp is including content.jsp.
<% int var=1;
request.setAttribute("username","Tom");
%>
<jsp:include page="content.jsp" />
content.jsp
<p>
Output from content.jsp:
<br>
var=<%=var %>
<br>
username=<%=request.getAttribute("username") %>
访问din.jsp时会报编译错误
org.apache.jasper.JasperException: Unable to compile class for JSP:
An error occurred at line: [4] in the jsp file: [/content.jsp]
var cannot be resolved to a variable
1: <p>
2: Output from content.jsp:
3: <br>
4: var=<%=var %>
5: <br>
6: username=<%=request.getAttribute("username") %>
原因:content.jsp无法识别局部变量var,因为这是动态包含,目标组件和源组件是不同的Servlet。两个Servlet之间是无法访问对方的服务方法中的局部变量的。
对于动态包含,Tomcat按以下流程响应客户端请求。
- 解析din.jsp(源)并翻译为Servlet源文件(.java),din.jsp中的
<jsp:include page="content.jsp"/>
被翻译为如下代码:
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "content.jsp", out, false);
- 把Servlet源文件编译为Servlet类(.class)。
- 初始化din.jsp对应的Servlet,再运行它的服务方法。
- 与din.jsp对应的Servlet的服务方法会调用org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, “content.jsp”, out, false)方法,当执行这个方法时,会解析content.jsp,如果没有语法错误,就翻译为Servelt源文件,再编译为Servlet类,运行服务方法。
- Servlet容器执行完JspRuntimeLibrary.include(request, response, “content.jsp”, out, false)方法后,继续执行din.jsp代表的Servlet的服务方法的后续代码。
<jsp:include />标签有一个flush属性,值为 true 或 false,true,表示源组件在包含目标组件之前就把响应正文提交给了客户端。
6.5.3 混合使用动态包含和静态包含
静态包含通常用来包含不会发生变化的网页内容,二动态包含通常用来包含会发生变化的网页内容。
6.6 JSP异常处理
JSP在发生异常时,可以通过以下指令将请求转发给另一个专门处理异常的页面:
<%@ page errorPage="errorpage.jsp"%>
<%@ page isErrorPage=“true”%> 指令将网页声明为异常处理页面。
抛出异常的网页和处理异常的网页之间是请求转发关系,所以,处理异常的网页可以直接访问exception隐含对象获取当前异常的详细信息。
<p>
错误原因:<%exception.printStackTrace(new PrinterWriter(out));%>
</p>
6.7 发布JSP
将JSP文件放到应用的根目录下即可。或者通过web.xml配置
<servlet>
<servlet-name>hi</servlet-name>
<jsp-file>/hello.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>hi</servlet-name>
<url-pattern>/hi</url-pattern>
</servlet-mapping>
6.8 预编译JSP
当JSP文件被客户端第一次请求访问时,Servlet容器需要先把JSP文件编译为Sevlet类才能运行,这一过程会延长客户的等待时间,所以,可以对JSP文件进行预编译。
6.9 PageContext类的用法
JSP文件中使用PageContext类的场合主要有:
- JSP文件中的Java片段。
- JS批文件中的自定义标签的处理类。
PageContext类中的方法可分为以下几种:
- 用于向各种范围内存取属性的方法
// 返回页面范围内特定属性的值
Object getAttribute(String name);
//返回参数scope指定范围内的特定的值
Object getAttribute(String name, int scope);
// 向参数scope指定的范围内存放属性
void setAttribute(String name, Object value, int scope);
// 删除scope指定范围内的特定属性
void removeAttribute(String name,int scope);
// 依次从页面范围、请求范围、会话范围和web应用范围内查找name指定的属性,如果找到,就立即返回,如果所有范围都不存在,则返回null
Object findAttribute(String name);
// 返回参数指定的属性的所属范围
int getAttributesScope(String name);
以上scope的取值为 PgaeContext的四个静态常量
public static final int PAGE_SCOPE = 1; //页面
public static final int REQUEST_SCOPE = 2; //请求
public static final int SESSION_SCOPE = 3; //会话
public static final int APPLICATION_SCOPE = 4; //web应用
PageContext对象是由容器创建的,所以JSP文件可以直接通过固定变量 pageContext来因哟呵你给隐含的PageContext对象。
- 用于获得由Servlet容器提供的其他对象的引用的方法。
// 返回当前JSP对应的Servlet实例
Object getPage();
ServletRequest getRequest();
ServletResponse getResponse();
ServletConfig getServletConfig();
ServletContext getServletContext();
HttpSession getSession();
//返回一个用于输出响应正文的jspWriter对象
JspWriter getOut();
在JSP文件的Java程序片段中可以直接通过该application、request等固定变量来引用PageContext、ServeltReqeust和ServeletResponse等对象,而在自定义的JSP标签中时不能使用的,此时就需要依靠pageContext对象来引用这些对象。
- 用于请求转发和包含的方法。
void forward(String relativeUrlPath) throws ServletException, IOException;
void include(String relativeUrlPath) throws ServletException, IOException;
6.10 在 web.xml 中配置JSP
<jsp-config>
<jsp-property-group>
<!--对JSP进行描述-->
<description>special property grouup for JSP Configurationn</description>
<!-- 设定改配置所影响的jsp,这里表示对所有以.jsp结尾的文件有效 -->
<url-pattern>*.jsp</url-pattern>
<!-- 为true表示不支持EL表达式-->
<el-ignored>true</el-ignored>
<page-encoding>GBK</page-encoding>
<!--为true表示不支持<%%>java程序片段-->
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
<jsp-property-group>
<!--对JSP进行描述-->
<description>special property grouup for JSP Configurationn</description>
<!-- 设定改配置所影响的jsp,这里表示对web引用下mypath目录中的文件有效 -->
<url-pattern>/mypath</url-pattern>
<!-- 为true表示不支持EL表达式-->
<el-ignored>true</el-ignored>
<!--自动包含jsp页面的头文件-->
<include-prelude>/include/head.jsp</include-prelude>
<!-- 自动包含JSP页面的尾部文件-->
<include-coda>/include/food.jsp</include-coda>
</jsp-property-group>
</jsp-config>
标签:Web,Java,文件,开发技术,jsp,jspx,JSP,Servlet,out 来源: https://blog.csdn.net/qq_43064950/article/details/109785977