常規JSP
JSP頁面最終會由容器來產生Servlet類的,比如Tomcat容器產生JSP的Servlet類放在work目錄裡。因此在JSP裡可以用很多簡化的文法供容器使用,這篇就來整理一下。
JSP文法
scriptlet:<% %>
可以在裡面寫Java代碼,如<%out.print("27");%>;
指令:<%@ %>
可以在頁面轉換時向容器給出特殊的指示;它有三個指令:page、include和taglib;而指令又有很多屬性如import是page的屬性:<%@ page import="foo.*,java.util.*"%>;
運算式:<%= %>
運算式會成為out.println()或out.print()的參數,因此<%=27%>等價於<%out.print(27);%>;
聲明:<%! %>
是JSP轉換成Servlet類的聲明,因此可以聲明類變數(靜態變數)、執行個體變數、方法等;
注釋:<%-- %>
就像Java代碼中的注釋一樣,在把JSP轉換成Servlet時會把注釋去掉;
隱式對象
JSP的隱式對象不僅僅有out,還有一些其他的:
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out;
final java.lang.Object page = this;
final javax.servlet.http.HttpServletRequest request;
final javax.servlet.http.HttpServletResponse response;
JSP產生的Servlet
在Tomcat的work目錄裡我們可以看到由JSP產生的Servlet類,該類繼承org.apache.jasper.runtime.HttpJspBase,其中有以下三個方法
_jspInit()
_jspDestroy()
_jspService()
他們分別由父類的init()、destroy()和service()方法調用;前面都帶有"_"號,表明我們不能去覆蓋他們,底線的意思是“不要碰我!”.如我用的是Tomcat7,在JSP中:
<%! public void _jspDestroy(){
int i=5;
}%>
會報異常:
org.apache.jasper.JasperException: Unable to compile class for JSP:
初始化JSP
通常,我們會把初始化參數分配給單獨的servlet或JSP頁面。這個指定的servlet或者JSP頁面通過ServletConfig的 getInitParameter方法來讀取這些參數。但在某些情況下,需要提供系統範圍內的初始化參數,任何servlet或者JSP頁面可以通過 ServletContext的getInitParameter方法來讀取這些初始化參數。但並不推薦這樣做,通常是使用MVC架構,在C中非常合適。
為JSP配置初始化參數是在<servlet>標記中增加一個<jsp-file>元素
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>qq</param-name>
<param-value>38602359</param-value>
</context-param>
<servlet>
<servlet-name>index</servlet-name>
<!-- 與Servlet的不同,使用的是jsp-file和URL -->
<jsp-file>/index.jsp</jsp-file>
<init-param>
<param-name>email</param-name>
<param-value>kevin@oseye.net</param-value>
</init-param>
</servlet>
</web-app>
而index.jsp
<%@page import="java.util.Enumeration"%>
<html>
<body>
<h2>Hello World!</h2>
<%=config.getInitParameter("email")%><br>
<%=getServletConfig().getInitParameter("email")%><br>
<%=application.getInitParameter("qq")%><br>
<%=getServletContext().getInitParameter("qq")%>
</body>
</html>
但輸出
Hello World!
null
null
38602359
38602359
可見並為取到JSP的初始化參數,但如果我在web.xml設定<servlet-mapping>就能取到
<servlet-mapping>
<servlet-name>index</servlet-name>
<url-pattern>/index.jsp</url-pattern>
</servlet-mapping>
這是為什麼呢?沒明白,先留著!
JSP屬性
JSP比普通的Servlet多了一個範圍,共有4個範圍
而pageContext的setAttribute和getAttribute都有重載,就多了一個int類型的範圍設定
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;
只能取到相應的範圍的屬性,但pageContext還有一個方法findAttribute,它從最嚴格的範圍查起,逐步轉向不那麼嚴格的範圍,也就是先在請求範圍尋找,再尋找會話範圍,最後尋找應用範圍,只要在一個作用查到就停止。例如
<html>
<body>
<h2>Hello World!</h2>
<%pageContext.setAttribute("name", "kevin",PageContext.REQUEST_SCOPE); %>
pageContext: <%=pageContext.getAttribute("name") %><br>
session: <%=session.getAttribute("name") %><br>
application: <%=application.getAttribute("name") %><br>
request: <%=request.getAttribute("name") %><br>
find: <%=pageContext.findAttribute("name") %><br>
</body>
</html>
輸出
Hello World!
pageContext: null
session: null
application: null
request: kevin
find: kevin
page指令
page指令的屬性有import、contentType、isThredSafe、Sessioin、buffer、autoflush、extends、info、errorpage、isErrorPage、language、pageEncoding等。格式:
<%@ page page_directive_attr_list %>
import屬性指定由Servlet匯入的package。
<%@ page import="package.class" %>
isThreadSafe屬性控制從JSP頁中產生的Servlet是否將實現SingleThreadModel介面。isThreadSafe屬性的使用採用以下兩種格式之一,前者是預設的:
<%@ page isThreadSafe = "True" %>
<%@ page isThreadSafe = "false" %>
session屬性控制頁面是否參與HTTP會話。
–預設值true,表明頁面將加入一個HTTPsession;
–設定為false,表明不會自動使用任何會話;
buffer屬性指明JspWriter的緩衝區大小。buffer屬性採用以下兩種格式之一:
<%@ page buffer="sizekb" %>
<%@ page buffer="none" %>
autoFlush屬性配合輸出緩衝區 buffer屬性使用。控制輸出緩衝區在裝滿時是否應該清除,或者當緩衝區溢位時是否應給出異常處理。
extends屬性指定為JSP頁產生的Servlet的超類。
<%@ page extends= "package.class" %>
info屬性定義一個通過getServletInfo方法可以從Servlet中檢索到的串。
<%@ page info= "some infomation" %>
isErrorpage屬性指明當前頁是否能充當其他JSP頁面的錯誤頁。預設值為false。
errorpage屬性指明如果拋出一個異常,而異常沒有被捕獲時,此錯誤處理所指向的URL。
contentType屬性指明字元編碼和JSP響應的MIME類型。contentType屬性的預設值為text/html ;charset屬性的預設值為 ISO-8859-1
<%@ page contentType="TYPE" %>
<%@ page contentType="TYPE; charset=CHARSET" %>
pageEncoding屬性定義了頁的編碼字元。除非指定page指令的contentType屬性,否則預設值為 ISO-8859-1。
language屬性指定將要使用的程式設計語言。
無指令碼JSP
使用scriptlet、運算式和聲明不僅代碼混了難以維護,而且不利於頁面設計人員和伺服器開發人員的分工合作,因此這裡介紹EL(運算式語言)、標準動作和JSTL。
標準動作
JSP動作使用格式為:<jsp:標記名>,利用XML文法格式的標記來控制Servlet引擎的行為。這些jsp標籤動作元素是在使用者要求階段執行的,這些標準動作元素是內建在jsp檔案中的,所以可以直接使用。有以下動作元素
<jsp:useBean> //定義jsp頁面使用一個JavaBean執行個體;
<jsp:setProperty> //設定一個JavaBean中的屬性值;
<jsp:getProperty> //從JavaBean中擷取一個屬性值;
<jsp:include> //在JSP頁麵包含一個外在檔案;
<jsp:forward> //清空緩衝區把到達的請求轉寄另一個頁面進行處理;
<jsp:param> //用於傳遞參數值;
<jsp:plugin> //用於指定在客戶瀏覽器中插入外掛程式的屬性;
<jsp:params> //用於向HTML頁面的外掛程式傳遞參數值;
<jsp:fallback> //指定如何處理用戶端不支援外掛程式啟動並執行情況;
這裡要稍作解釋JavaBean的概念,到底什麼是bean法則呢?
就是遵循”古老“JavaBeans規範的法則。我們說的是JavaBean,而不是企業JavaBean(EJB),這兩個東西完全不相干(要搞清楚)。普通的非企業JavaBean規範定義了一個類怎麼才算是JavaBean。儘管這個規範確實很複雜,不過,結合JSP和servlet使用bean時,你只要知道以下規則就行了(只列出了與使用servlet和JSP相關的規則):
必須有一個無參公用建構函式;
必須按命名規範來命名公用的擷取方法和設定方法,首先是"get"(或者如果是一個布爾性質,擷取方法的首碼是"is")和"set",如getName何setName。要獲得性質名,先去掉get和set,並把首字母小寫。
設定方法的參數類型和擷取方法的傳回型別必須一樣,如String getName()和void setName(String name)。
性質名和類型是由get和set方法推導得出,而不是得自於類中的一個成員。
結合JSP使用時,性質類型必須是String,或者是一個基本類型。如果不是這樣,儘管也許是一個合法的bean,可如此一來,你可能還得使用指令碼。
例如我有一個JavaBean
package net.oseye;
public class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
再servlet的doGet()中
Person p=new Person();
p.setName("kevin");
p.setAge(22);
request.setAttribute("person", p);
RequestDispatcher view=request.getRequestDispatcher("/index.jsp");
view.forward(request, response);
然後在index.jsp中使用指令碼可能是這樣的
<%
Person p=(Person)request.getAttribute("person");
if(p!=null){
out.print(p.getName());
}else{
out.print("null");
}
%>
而如果使用JSP標準動作是
<jsp:useBean id="person" class="net.oseye.Person" scope="request" />
<jsp:getProperty property="name" name="person"/>
對標準動作的一點點說明(最好查看Tomcat/work目錄產生的java檔案更好來理解):
<jsp:useBean id="person" class="net.oseye.Person" scope="request" />
id為屬性名稱,class為屬性值的類型,scope為屬性範圍;
如果屬性不存在person,則建立一個新的屬性,是class的樣本對象;因此它也可以有體來設定性質值
<jsp:useBean id="person2" class="net.oseye.Person" scope="request">
<jsp:setProperty property="name" name="person2" value="kevin2"/>
</jsp:useBean>
<%=((Person)request.getAttribute("person2")).getName()%>
帶體的只有找不到相應的屬性時才建立。
如果建立多態JavaBean的話使用type屬性,含義是
type id=new class();
type是參考型別,而class為物件類型,因此type可以是介面、抽象類別、父類等等,如果沒有type其實預設的是type和class一樣。如果只有type,而沒有class,屬性bean必須存在。
scope預設為“page”。
<jsp:param>
param是用於接收form表單提交的參數,如果表單元素名和JavaBean的性質名不同,則需要使用param,如form表單
<form method="get" action="http://localhost:8080/mytest/Index">
<input name="username" type="text" value="Anders" />
<input type="submit" name="Submit" value="提交" />
</form>
在index.jsp中
<jsp:useBean id="person" class="net.oseye.Person" scope="request">
<jsp:setProperty property="name" name="person" param="username" />
</jsp:useBean>
如果表單的元素名稱和JavaBean的姓名相同,就可以省略param參數,如果表單的元素與JavaBean的性質完全符合還可以在property使用萬用字元
<jsp:useBean id="person" class="net.oseye.Person" scope="request">
<jsp:setProperty property="*" name="person" />
</jsp:useBean>
EL
如在表中動作中所說,如果JavaBean的性質類型不是String的話,儘管它是一個合法的JavaBean但仍需要使用指令碼,其實還有一種方式就是EL(express language)。EL文法相當簡單,格式是:
${firstThing.secondThing}
其中firstThing可以是隱式對象:pageScope、requestScope、sessionScope、applicationScope、pageContext、param、paramValues、header、headerValues、cookie、initParam,或屬性
pageScope屬性、requestScope屬性、sessionScope屬性、applicationScope屬性。
訪問映射值或性質時可以使用點號(.),但除了性質和映射值外還有很多的其他特殊的,隨意最通用的是中括弧([]),如在servlet中
Person p=new Person();
p.setName("kevin");
p.setAge(22);
request.setAttribute("person", p);
RequestDispatcher view=request.getRequestDispatcher("/index.jsp");
view.forward(request, response);
在JSP中
<%@page isELIgnored="false" %>
<html>
<body>
${person.name}<br>
${person["name"]}<br>
${10+10 }
</body>
</html>
PS:由於版本的不同,可能預設啟用了EL,或者預設沒啟用EL,如果預設沒啟用需要手動啟用,所以第一行不能少的,當然也可以在DD檔案中配置。而且EL還可以自訂函數,這點先不深入了。
JSTL
JSTL全名為JavaServer Pages Standard Tag Library。JSLT標籤庫,是日常開發經常使用的,也是眾多標籤中效能最好的。需要添加JSTL的JAR包
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
然後在JSP文檔中添加聲明
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
然後就可以使用JSTL了
<c:out value="dddd"></c:out>
常用的標籤:如<c:out>、<c:remove>、<c:catch>、<c:set>等;
條件標籤:如<c:if><c:when>、<c:choose>、<c:otherwise>等;
URL標籤:如<c:import>、<c:redirect>和<c:url>等;
XML標籤:如<xml:out>等;
國際化輸出標籤:如<fmt:timeZone>等;
SQL標籤:如<sql:query>、<sql:update>、<sql:transaction>等;
當然,如果JSTL不夠用的話,還可以定製一些JSTL,定製JSTL暫時不深入。
PS:include指令、<jsp:include>標準動作和JSTL的不同:前者是在編譯期複製,後者是在執行期複製。