JSP 網頁最終將轉換為 Servlet,所謂的 JavaBean,實際上也是 Servlet 中的一個物件實例,當你使用 <jsp: useBean>
時,實際上就是在宣告一個 JavaBean 的物件,id
屬性即是用以指定參考名稱,而 class
屬性則是型態名稱。例如若你在 JSP 的頁面中撰寫以下的內容:
<jsp:useBean id="user" class="cc.openhome.User" />
實際在轉譯為 Servlet 之後,會產生以下的程式碼片段:
cc.openhome.User user = null; // id="user" 就是產生這邊的user參考名稱
synchronized (request) {
user = (cc.openhome.User) _jspx_page_context.getAttribute(
"user", PageContext.PAGE_SCOPE);
if (user == null){
user = new cc.openhome.User();
_jspx_page_context.setAttribute(
"user", user, PageContext.PAGE_SCOPE);
}
}
其中 _jspx_page_context
參考至 PageContext
物件,也就是說,使用 <jsp:useBean>
標籤時,會在屬性範圍(預設是 page
範圍)中尋找有無 id
名稱所指定的屬性,如果找到就直接使用,如果沒有找到就建立新的物件。
你可以在使用 <jsp:useBean>
標籤時,使用 scope
屬性來指定其儲存的屬性範圍,可以指定的值有 page
(預設)、request
、session
與 application
。例如:
<jsp:useBean id="user" class="cc.openhome.User" scope="session"/>
則轉譯後的 Servlet 中將會有以對的程式碼片段,也就是改從會話範圍中尋找指定的屬性:
cc.openhome.User user = null;
synchronized (request) {
user = (cc.openhome.User) _jspx_page_context.getAttribute(
"user", PageContext.SESSION_SCOPE);
if (user == null){
user = new cc.openhome.User();
_jspx_page_context.setAttribute(
"user", user, PageContext.SESSION_SCOPE);
}
}
注意!如果你使用 <jsp:useBean>
標籤時沒有指定 scope
,則預設「只」在 page
範圍中尋找 JavaBean,找不到就建立新的 JavaBean 物件(不會再到 request
、session
與 application
中尋找)。
在轉譯後的 Servlet 程式碼中,如果想指定宣告 JavaBean 時的型態,則可以使用 type
屬性。例如:
<jsp:useBean id="user"
type="cc.openhome.BaseUser" class="cc.openhome.User"
scope="session"/>
如此產生的 Servlet 程式碼中,將會有以下的片段:
cc.openhome.BaseUser user = null;
synchronized (request) {
user = (cc.openhome.BaseUser) _jspx_page_context.getAttribute(
"user", PageContext.SESSION_SCOPE);
if (user == null){
user = new cc.openhome.User();
_jspx_page_context.setAttribute(
"user", user, PageContext.SESSION_SCOPE);
}
}
type
屬性的設定可以是一個抽象類別,也可以是一個介面。如果你只設定 type
而沒有設定 class
屬性,則必須確定在某個屬性範圍中已經存在所要的物件,否則會發生 InstantiationException
例外。
標籤的目的是減少 JSP 中 Script 的使用,所以反過來說,如果你發現 JSP 中有 Scriptlet,撰寫的是從某個屬性範圍中取得物件,則可以思考一下,是否可以用 <jsp:useBean>
來消除 Scriptlet 的使用。
在使用 <jsp:useBean>
標籤取得或建立 JavaBean 實例之後,若要設值給 JavaBean,則可以使用 <jsp:setProperty>
標籤,你可以使用幾個方式來進行設定。例如:
<jsp:setProperty name="user" property="password" value="123456" />
這會在產生的 Servlet 程式碼中,使用 PageContext
的 findAttribute()
,從 page
、request
、session
、application
依序找看看有無 name
所指定的屬性名稱,找到的話,再透過反射(Reflection)機制找出 JavaBean 上的 setPassword()
方法,呼叫並將 value
的指定值設定給JavaBean。
如果想要將請求參數的值設定給 JavaBean 的某個屬性,則以下是個範例:
<jsp:setProperty name="user" param="password" property="password" />
如果請求參數中包括 password
,則會透過 JavaBean 的 setPassword()
方法設定給 JavaBean 實例。你也可以不指定請求參數名稱,而由 JSP 的自省(Introspection)機制來判斷是否有相同的請求參數名稱,如果有的話就自動找出對應的設值方法並呼叫以設值給 JavaBean。例如以下會找看看有無 password
請求參數,有的話就設定給 JavaBean:
<jsp:setProperty name="user" property="password" />
<jsp:setProperty>
有個最有彈性的寫法,就是將請求參數名稱與 JavaBean 的屬性名稱交給自省機制來自動匹配。例如:
<jsp:setProperty name="user" property="*" />
如果你的 JavaBean 屬性是整數、浮點數之類的基本型態,自省機制可以自動轉換請求參數字串為對應屬性的基本資料型態。
你也可以在使用 <jsp:useBean>
時一併設定屬性值,例如:
<jsp:useBean id="user" class="cc.openhome.User" scope="session">
<jsp:setProperty name="user" property="*" />
</jsp:useBean>
如此一來,如果屬性範圍中找不到 user
時,則會新建一個物件並設定其屬性值;如果可以找到物件的話就直接使用。也就是轉譯後產生以下的程式碼:
cc.openhome.User user = null;
synchronized (request) {
user = (cc.openhome.User) _jspx_page_context.getAttribute(
"user", PageContext.SESSION_SCOPE);
if (user == null){
user = new cc.openhome.User();
_jspx_page_context.setAttribute(
"user", user, PageContext.SESSION_SCOPE);
org.apache.jasper.runtime.JspRuntimeLibrary.introspect(
_jspx_page_context.findAttribute("user"), request);
}
}
這與撰寫以下的內容是有點不同的:
<jsp:useBean id="user" class="cc.openhome.User" scope="session"/>
<jsp:setProperty name="user" property="*" />
如果使用以上的寫法,則無論是找到或新建 JavaBean 物件,都一定會使用內省機制來設值,也就是轉譯的 Servlet 程式碼中會有以下片段:
cc.openhome.User user = null;
synchronized (request) {
user = (cc.openhome.User) _jspx_page_context.getAttribute(
"user", PageContext.SESSION_SCOPE);
if (user == null){
user = new cc.openhome.User();
_jspx_page_context.setAttribute(
"user", user, PageContext.SESSION_SCOPE);
}
}
org.apache.jasper.runtime.JspRuntimeLibrary.introspect(
_jspx_page_context.findAttribute("user"), request);
標籤的目的是減少 JSP 中 Script 的使用,所以反過來說,如果你發現 JSP 中有 Scriptlet,有透過設值方法(Setter)對 JavaBean 作設值的動作,則可考慮使用 <jsp:setProperty>
來消除 Scriptlet 的使用。
<jsp:getProperty>
的使用比較單純,在使用 <jsp:useBean>
標籤取得或建立 JavaBean 實例之後,基本上就只有一種用法:
<jsp:getProperty name="user" property="name"/>
這會使用透過 PageContext
的 findAttribute()
找出 user
屬性,並透過 getName()
方法取得值以顯示在網頁上,也就是轉譯後的 Servlet 原始碼中會有以下片段:
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString(((
(cc.openhome.User)_jspx_page_context.findAttribute("user"))
.getName()
)));
在使用 <jsp:useBean>
標籤取得或建立 JavaBean 實例之後,由於 <jsp:setProperty>
與 <jsp:getProperty>
轉譯後,都是使用 PageContext
的 findAttribute()
來尋找屬性,因此尋找的順序是頁面、請求、會話、應用程式範圍。
標籤的目的是減少 JSP 中 Script 的使用,所以反過來說,如果你發現 JSP 中有 Scriptlet,有透過取值方法(Getter)對 JavaBean 作取值的動作,則可考慮使用 <jsp:getProperty>
來消除 Scriptlet 的使用。