Petstore源碼追蹤記(3)-商業邏輯處理(四)
最後更新:2018-07-26
來源:互聯網
上載者:User
Petstore 源碼記縱記 (3) -商業邏輯處理 ( 下 )
( 歐宣修 )
圖文並茂版請參考
http://www.javatwo.net/JavaPaper/Petstore-3_business_logic.pdf
接續上期 ...
我們已瞭解 SignOnFilter 在 Web tier 處理登入工作的步驟,它需要透過 EJB tier 從資料庫讀取資料進行比對,所以接下來探討在 EJB tier 的運作情形,從圖 14 、 15 可找出實際對應的 EJB ,從圖上面可知此 EJB 的屬性是 Local Stateless
Session Bean ,這很少見,大部份的書介紹到 Local Bean 的用法都用在 Entity Bean ,由此可知 Local Bean 的用法亦可用在 Session Bean 。
SignOnEJB ,源碼在 Petstore_home/src/components/signon/src/com/sun/j2ee/blueprints/signon/ejb/SignOnEJB.java ,請讀者順便加上偵察程式碼:
public class SignOnEJB implements SessionBean {
private static final String USER_HOME_ENV_NAME =
"java:comp/env/ejb/local/User";
private InitialContext ic = null;
private UserLocalHome ulh = null;
public void ejbCreate() throws CreateException {
try {
ic = new InitialContext();
// 取得 UserLocalHome Reference ,它是代表使用者基本資料的 Local Entity Bean
ulh = (UserLocalHome) ic.lookup(USER_HOME_ENV_NAME);
} catch (NamingException ne) {
throw new EJBException("SignOnEJB Got naming exception! " +
ne.getMessage());
}
}
/**
* 此函數由 SignOnFilter 呼叫,依使用者帳號找出對應的 User 實體
*(instance) ,然後呼叫 User 實體的密碼比對函數- user.matchPassword()
* business method used to check if a user is allowed to sign on
*/
public boolean authenticate(String userName, String password) {
// 請加入偵察程式碼,方便稍候程式驗證
System.out.println("SignOnEJB 執行 authenticate() 進行使用者驗證
userName="+userName+", password="+password);
try {
UserLocal user = ulh.findByPrimaryKey(userName);
return user.matchPassword(password);
} catch (FinderException fe) {
return false; // User not found, so authentication failed.
}
}
以下略 ...
圖 16 SignOnEJB 的 EJB Reference
UserEJB ,源碼在
Petstore_home/src/components/signon/src/com/sun/j2ee/blueprints/signon/user/ejb/
UserEJB.java ,它是 Local Entity Bean ,對應資料庫中實際資料表-
UserEJBTable 在約 88 列可看到 SignOnEJB 所呼叫的函數,請讀者順便加
上偵察程式碼:
public boolean matchPassword(String password) {
// 請加入偵察程式碼,方便稍候程式驗證
System.out.println("UserEJB 執行 matchPassword() 進行密碼比對 ");
return password.equals(getPassword());
}
圖 17 UserEJB 為 Entity Bean
點選圖 17 右下角 ”Deployment Settings” 鈕,可見到圖 18 畫面,
選擇左下角 ”Method Implementation Queries” 之 ”Container Methods”
之 ”createRow” ,即可看到圖 18 之 SQL Query :
圖 18 UserEJB 對應資料表為 UserEJBTable
由上述所列程式碼及圖,讀者應該可以瞭解使用者登入在 EJBz tier 的運作情形,在 UserEJB 也看到一個不一樣的用法,就是在 Entity Bean 上使用商業邏輯- matchPassword() 函數,在大部份的 EJB 書籍或檔案都告訴我們商業邏輯應該用 Session Bean 去實作,資料則用 Entity Bean 來實作,但在這裡密碼比較的商業邏輯是非常簡單的,只用了一行程式就解決了,所以也不需為了它另外再做一個 Session Bean ,
也許讀者會問為什麼不將此函數寫在 SignOnEJB 。筆者認為密碼比對不只在登入流程上會使用,在使用者基本資料編輯時也可能會用到,所以還是放在 UserEJB 比較適合: ) 我們能夠知道使用者基本資料是存於 UserEJBTable ,我們要如何知道此資料表所存內容是什麼。請參閱注 4 。
現在請重複第一階段驗證步驟將程式重新編輯及部署,可發現程式如我們所預期般執行。
圖 19 第二階段程式驗證結果
第三階段
大家還記得在 SignOnFilter 之 validateSignOn() 函數,驗證成功會將
SIGNED_ON_USER 變數 ( 對應實際變數為 j_signon) 的值設為真值 (true) :
hreq.getSession().setAttribute(SIGNED_ON_USER, new Boolean(true));
當我們再次從首頁進入使用者基本資料瀏覽頁,會再度給 SignOnFilter 攔截,從 Session 取出 SIGNED_ON_USER 變數 ( 對應實際變數為 j_signon) ,經判斷為真值 (true) , SignOnFilter 則會允許存取轉導至使用者基本資料瀏覽畫面 (customer.do) ,讀者可參考下列程式碼加入偵察碼,在約 125 列 doFilter() 函數:
boolean signedOn = false;
if (hreq.getSession().getAttribute(SIGNED_ON_USER) != null) {
signedOn
=((Boolean)hreq.getSession().getAttribute(SIGNED_ON_USER)).booleanValue();
// 加入偵察碼
System.out.println("signedOn="+signedOn);
} else {
hreq.getSession().setAttribute(SIGNED_ON_USER, new Boolean(false));
}
// jump to the resource if signed on
// 若已登入過,則結束此 Filter 工作,進入 Filter chain ,以本例來說,它為
Filter chain 中最後一個 Filter ,所以就是不做任何事,讓使用者進入他的目的畫面
if (signedOn) {
// 加入偵察碼
System.out.println(" 使用者已登入過。 ");
chain.doFilter(request,response);
return;
}
參考前面敘述重新編譯部署後執行,可得下圖預期結果:
圖 20 第三階段程式驗證結果
customer.do
到這裡相信讀者能對 Petstore 登入流程式控制管有更深入的瞭解,通過登入流程就到達了使用者基本資料瀏覽畫面 (customer.do) , *.do 與前二篇介紹的 *.screen 不同, *.screen 代表的是一個畫面,如 main.screen 代表首頁,它可由多個 .jsp 所組成; *.do 代表的是一個動作, customer.do 代表對使用者基本資料相關動作,如新增、修改、刪除,它會透過 EJB tier 與資料庫互動,最後得到的結果也是要展現,運用 *.screen 的機制組成結果畫面,所以我們可以這樣想象 *.screen 是名詞, *.do 是動詞,以本例來說,只是讀取資料,雖然運用到 *.do ,但並沒有任何動作,為了能讓讀者瞭解整個架構,還是在此稍事說明。
請開 ?deploytool ,點選左邊窗格 Files > Applications > PetstoreEAR > PetstoreWAR > MainServlet ,選擇右邊 Alias 頁,可發現處理 *.do 即是 MainServlet
圖 21 *.do 對應 MainServlet
點選 General 頁可找到實際對應類別,源碼在
Petstore_home/src/waf/src/controller/com/sun/j2ee/blueprints/waf/controller/web/MainServlet.java
,在約 79 列可找到初始函數:
public void init(ServletConfig config) throws ServletException {
// 讀取預設語系,值為 ”en_US”
String defaultLocaleString = config.getInitParameter("default_locale");
defaultLocale = I18nUtil.getLocaleFromString(defaultLocaleString);
this.context = config.getServletContext();
String requestMappingsURL = null;
try {
// 讀取 mapping.xml
requestMappingsURL =
context.getResource("/WEB-INF/mappings.xml").toString();
} catch (java.net.MalformedURLException ex) {
System.err.println("MainServlet: initializing ScreenFlowManager
malformed URL exception: " + ex);
}
// 將 mappings.xml 轉成 HashMap 類別並存入 ServletContext
urlMappings = URLMappingsXmlDAO.loadRequestMappings(requestMappingsURL);
context.setAttribute(WebKeys.URL_MAPPINGS, urlMappings);
eventMappings = URLMappingsXmlDAO.loadEventMappings(requestMappingsURL);
context.setAttribute(WebKeys.EVENT_MAPPINGS, eventMappings);
//ScreenFlowManager 初始化並存入 ServletContext
getScreenFlowManager();
//RequestProcessor 初始化並存入 ServletContext
getRequestProcessor();
}
圖 22 MainServlet 實際對應類別
MainServlet 在初始化時會讀取預設語系及對應設定檔