javascript
Head First JSP---随笔五
作為JSP
JSP變成Servlet。這個servlet不用你來創建。容器會查看你的JSP,把它轉換成Java源代碼,在編譯成完整的Java servlet類。
JSP技術模型
6.1 識別或描述以下元素,或為以下元素編寫JSP代碼:
6.2 編寫使用以下指令的JSP代碼:
6.3 編寫使用了正確語法的JSP文檔(基于XML的文檔)
6.4 描述頁面生命周期的作用和事件序列:
6.5 給定一個設計目標,使用適當地隱式對象編寫JSP代碼:
6.6 配置部署描述文件來聲明一個或多個標記庫,禁用計算(表達式)語言,以及禁用腳本語言
6.7 給定一個特定的設計目標,要求將一個JSP片段包含在另一個頁面中,編寫JSP代碼使用最合適的包含機制(include指令或jsp:include標準動作)
JSP變成一個servlet
這一章將解決下面幾個問題:
建立一個JSP顯示被訪問了多少次
開發環境:
BasicCounter.jsp文件代碼:
Counter.java文件代碼:
package foo;public class Counter {private static int count = 0;public static synchronized int getCount() {count++;return count;} }XML文件代碼:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID" version="3.0"><welcome-file-list><welcome-file>/BasicCounter.jsp</welcome-file></welcome-file-list></web-app>實驗結果:
使用page指令導入包
我們發現寫foo有點麻煩。所以,使用page指令導入包。將JSP文件代碼更改為:
<%@ page import="foo.*" %> <!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"> </head> <body> The page count is: <% out.println(Counter.getCount()); %> </body> </html>實驗結果:
指令有3種:
page允許你導入包。導入多個包<%@ page import="foo.*,java.util.*" %>
記住:<%@ page import = ? %>中的page是指令,import是page的屬性,?是屬性的值。
表達式
我們在JSP代碼里面使用了out.println(),我們應該盡量的避免這樣的做法。所以可以更改JSP代碼:
<%@ page import="foo.*" %> <!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"> </head> <body> The page count is: <%= Counter.getCount() %> </body> </html>實驗結果:
我們已經看到3種不同類型的JSP元素:
容器看到<%= Counter.getCount()%>就會把它轉換為out.print(Counter.getCount());。
如果看到的是<%= Counter.getCount();%>,注意最后一個分號,容器會轉換成out.print(Counter.getCount(););這種樣子,明顯會報錯。
要記住:不要在表達式的最后加分號(還有要注意的地方就是,表達式不能輸出一個void的類型,想想如果調用一個返回值為void的方法,就會報錯)。
一個炸彈
修改JSP文件代碼如下:
<%@ page import="foo.*" %> <!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"> </head> <body> //定義一個變量是count,JSP是一個servlet <% int count = 0; %> The page count is: //count+1 <%= ++count %> </body> </html>我們認為JSP是一個servlet,用scriptlet聲明一個變量,然后去使用它。結果剛剛開始的時候還不錯,如下:
第二次訪問:
發現怎么和結果1等同呢?
于是我們查看jsp最后會變成一個什么樣的servlet代碼。如下:
/** Generated by the Jasper component of Apache Tomcat* Version: Apache Tomcat/9.0.4* Generated at: 2018-08-02 12:04:37 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.*; //與servlet的差別 import javax.servlet.jsp.*; //導入的包 import foo.*;//繼承也不一樣 public final class BasicCounter_jsp extends org.apache.jasper.runtime.HttpJspBaseimplements 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("javax.servlet.http");_jspx_imports_packages.add("foo");_jspx_imports_packages.add("javax.servlet.jsp");_jspx_imports_classes = null;}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() {}//service方法public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)throws java.io.IOException, javax.servlet.ServletException {final java.lang.String _jspx_method = request.getMethod();if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");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");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("<!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("</head>\r\n");out.write("<body>\r\n");//我們寫的代碼,定義一個變量int count = 0; out.write("\r\n");out.write("The page count is:\r\n");//我們寫的代碼,然count+1out.print( ++count );out.write("\r\n");out.write("</body>\r\n");out.write("</html>");} 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);}} }由上面代碼的注釋就知道了,原來我們定義的count是局部變量。因此我們知道了我們的原因。
另一個JSP元素
我們想要聲明一個全局變量。
將JSP代碼修改為(<%! %>設置全局變量):
<%@ page import="foo.*" %> <!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"> </head> <body> //設置全局變量 <%! int count = 0; %> The page count is: <%= ++count %> </body> </html>我們在看看JSP.java文件的代碼:
/** Generated by the Jasper component of Apache Tomcat* Version: Apache Tomcat/9.0.4* Generated at: 2018-08-02 12:20:55 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 foo.*;public final class BasicCounter_jsp extends org.apache.jasper.runtime.HttpJspBaseimplements org.apache.jasper.runtime.JspSourceDependent,org.apache.jasper.runtime.JspSourceImports {//count變成了全局變量int count = 0; 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("javax.servlet.http");_jspx_imports_packages.add("foo");_jspx_imports_packages.add("javax.servlet.jsp");_jspx_imports_classes = null;}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 {final java.lang.String _jspx_method = request.getMethod();if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");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");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("<!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("</head>\r\n");out.write("<body>\r\n");out.write("\r\n");out.write("The page count is:\r\n");out.print( ++count );out.write("\r\n");out.write("</body>\r\n");out.write("</html>");} 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);}} }正如上面代碼所看見的,count變成了全局變量。實驗效果很好,會一直累計訪問次數。
方法聲明
修改JSP文件代碼:
<%@ page import="foo.*" %> <!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"> </head> <body> //JSP中定義方法 <%! int doubleCount(){count = count*2;return count; } %> <%! int count = 0; %> The page count is: <%= ++count %> </body> </html>JSP.java文件在int count的上方增加了下面這樣的一段代碼:
//方法定義 int doubleCount(){count = count*2;return count; } //全局變量 int count = 0;于是乎,我們好像知道了JSP就是一個servlet。
容器如何處理JSP
隱式對象
| JspWriter | out |
| HttpServletRequest | request |
| HttpServletResponse | response |
| HttpSession | session |
| ServletContext | application |
| ServletConfig | config |
| Throwable | exception |
| PageContext | pageContext |
| Object | page |
我們這里說說第4個作用域:pageContext(頁面級作用域)。
其中PageContext封裝了其他隱式對象,所以如果向某些輔助對象提供一個PageContext引用,這些輔助對象就可以使用這個PageContext引用得到其他隱式對象的引用以及所有作用域的屬性。
再說說exception,只有指定“錯誤頁面”才能用到這個隱式對象。
練習
開發環境:
hobby.html代碼:
Hobby.java這個servlet代碼:
package foo;import java.io.IOException; import java.util.ArrayList;import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public class Hobby extends HttpServlet{private static final long serialVersionUID = 1L;@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("html/text");ArrayList<String> al = new ArrayList<String>();al.add("Fred");al.add("Pradeep");al.add("Philippe");request.setAttribute("names", al);request.getRequestDispatcher("/BasicCounter.jsp").forward(request, response);} }BasicCounter.jsp代碼:
<%@ page import="java.util.*" %> <html> <body>The friends who share your hobby of<%= request.getParameter("hobby") %>are:<br><% ArrayList al = (ArrayList)request.getAttribute("names");%><% Iterator it = al.iterator(); %><% while(it.hasNext()){ %><%= it.next() %> <br><% } %> </body> </html>XML代碼:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID" version="3.0"><servlet><servlet-name>hobby</servlet-name><servlet-class>foo.Hobby</servlet-class></servlet><servlet-mapping><servlet-name>hobby</servlet-name><url-pattern>/HobbyPage.do</url-pattern></servlet-mapping><welcome-file-list><welcome-file>/hobby.html</welcome-file></welcome-file-list></web-app>實驗結果:
點擊提交后:
所生成servlet的API
JSP的UML圖:
3個關鍵方法:
JSP生命周期
這里指出:Web應用包含JSP,部署這個應用時,在JSP生命周期中,整個轉換和編譯步驟只發生一次。
初始化JSP
我們想著既然JSP也是servlet,那么我們能不能能不能像初始化Servlet一樣初始化JSP呢?該怎么做呢?
開發環境:
配置servlet初始化參數(DD)
覆蓋jspInit()
<html> <body><%! public void jspInit(){ServletConfig sConfig = getServletConfig();String emailAddr = sConfig.getInitParameter("email");ServletContext ctx = getServletContext();ctx.setAttribute("mail", emailAddr);}%><%= config.getInitParameter("email") %><%= application.getAttribute("mail") %> </body> </html>實驗結果如下:
JSP中的屬性
| 應用 | getServletContext() | application |
| 請求 | request | request |
| 會話 | request.getSession() | session |
| 頁面 | 不適用 | pageContext |
通常我們不需要關系頁面作用域,除非我們在開發定制標記。
PageContext的UML圖:
使用pageContext的實例
設置一個頁面作用域屬性
<% Float one = new Float(42,5);pageContext.setAttribute("foo",one); %>獲取一個頁面作用域屬性
<%= pageContext.getAttribute("foo")%>使用pageContext設置一個會話作用域屬性
<%Float two = new Float(22.4);pageContext.setAttribute("foo",two,PageContext.SESSION_SCOPE); %>使用pageContext獲得一個會話作用域屬性(等價于session.getAttribute(“foo”))
<%= pageContext.getAttribute("foo",PageContext.SESSION_SCOPE)%>使用pageContext獲得一個應用作用域屬性(等價于application.getAttribute(“mail”))
<%= pageContext.getAttribute("mail",PageContext.APPLICATION_SCOPE)%>使用pageContext,即使不知道作用域也可以查找一個屬性(查找順序:先從請求作用域查找,再查找會話作用域,最后查找應用作用域。先找到哪一個就算“贏”)
<%= pageContext.findAttribute("foo")%>JSP的3個指令
| page指令 | <%@ page import=”foo.*” session=”false”%> | 定義頁面特定的屬性,如字符編碼、頁面響應的內容類型,已經這個頁面是否要有隱式對象。page指令可以使用至多13個不同屬性(如import屬性) |
| taglib指令 | <%@ taglib tagdir=”/WEB-INF/tags/cool” prefix=”cool”%> | 定義JSP可以使用標記庫 |
| include指令 | <%@ include file=”wickedHeader.html”%> | 定義在轉換時增加到當前頁面的文本和代碼。從而允許你建立可重用的塊,這些可重用的塊能增加到各個頁面上,而不必在每個JSP中重復寫所有這些代碼 |
page指令的屬性
| import | 導包 |
| isThreadSafe | 定義生成的servlet是否需要實現SingleThreadModel(最好不要這樣做) |
| contentType | 定義JSP響應的MIME內容 |
| isELIgnored | 定義轉換(.jsp到.java)這個頁面時是否忽略EL表達式 |
| errorPage | 定義一個資源的URL,如果有未捕獲到的Throwable,就會發送到這個資源。如果這個屬性指定了一個JSP,該JSP的page指令中就會有isErrorPage=”true”屬性 |
| language | 定義scriptlet、表達式和聲明中使用的腳本語言(現在就只有一個值,就是”java”) |
| extends | JSP會變成一個servlet類,這個屬性則定義了這個類的超類(一般不會使用這個屬性) |
| session | 定義頁面是否有一個隱式的session對象。默認值為”true” |
| buffer | 定義隱式out對象如何處理緩存 |
| autoFlush | 定義緩存的輸出是否自動刷新輸出。默認值為”true” |
| info | 定義放到轉換后頁面中的串,這樣就能使用所生成servlet繼承的getServletInfo()方法來得到這個信息 |
| pageEncoding | 定義JSP的字符編碼。默認為”ISO-8859-1” |
EL
HTML開發員不懂Java,所以不能再JSP里面寫這種Java代碼。
于是,EL出現了,它的出現完美的解決下面2個問題:
EL小試牛刀
EL表達式:${applicationScope.mail}等價于<%= application.getAttribute("mail")%>
禁用scriptlet
在DD中配置:
<web-app ...><jsp-config><jsp-property-group>//作用于所有的.jsp文件<url-pattern>*.jsp</url-pattern>//只要配置上這個就行了,記住true是禁用scriptlet<scripting-invalid>true</scripting-invalid></jsp-property-group></jsp-config> </web-app>禁用EL表達式
兩種方式:
(1)在DD中配置:
<web-app ...><jsp-config><jsp-property-group>//作用于所有的.jsp文件<url-pattern>*.jsp</url-pattern><el-ignored>true</el-ignored></jsp-property-group></jsp-config> </web-app>(2)在JSP中用page指令(注意前面還有一個is):
<%@ page isELIgnored="true"%>初見JSP的“動作”
標準動作:<jsp:include page="wickedFooter.jsp">
其他動作:<c:set var="rate" value="32">
看看動作的語法,把它與其他類型JSP元素的語法做個比較。我們回答下面問題:
(1)動作元素與scriptlet有什么區別?
本質上沒有區別,只是代碼形式改變了。
(2)看到一個動作時,怎么才能認出來?
不知道= =
本章完
總結
以上是生活随笔為你收集整理的Head First JSP---随笔五的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国企业海外人才发展白皮书
- 下一篇: 传统品牌vs新消费品牌社交营销差异化分析