Petstore原始碼追蹤記(3)-商業邏輯處理(一) (轉)

worldblog發表於2007-12-15
Petstore原始碼追蹤記(3)-商業邏輯處理(一) (轉)[@more@]

作者:歐宣修

 

並茂版請參考
two/JavaPaper/Petstore-3_business_logic.pdf">

前言
透過前面的介紹,我們能夠了解Petstore中Model、Controller、View是如何相互合作,現在讓筆者來說明Petstore商業邏輯(Business Logic)的處理方式,首先讓我們先了解為什麼要將商業邏輯與資料展現(Presentation)分開,如此做有下列的好處:


1.減少變動的衝擊:商業邏輯及資料展現彼此獨立,不因商業邏輯改變而影響資料展現的程式程式碼,反之亦然。
2.易於維護:將商業邏輯集中管理,一旦日後有修改,僅須修改一個地方。
3.重複使用:商業邏輯若與資料展現層程式混在一起,如此商業邏輯只能服務一種使用者介面(Client),若將兩者分離,我們可以很容易擴充第二種使用者介面。
4.各司其職:資料展現的設計人員與商業邏輯的設計人員通常是不一樣的,彼此獨立讓術業有專攻,品質及皆能提升。

隱形角色
在追縱Petstore商業邏輯處理方式前,我們先來看看Petstore中的隱形角色,這些隱形角色在(RI)啟用時即默默準備好,待適當時機為Petstore中商業邏輯處理服務,所以筆者須先介紹它們。


請開啟
Petstore_home(注一)srcappspetstoresrcdoc-INFweb.,
我們可以看到:
<!-- Encoding Filter Declaration Start --&gt
  //1.第一個Fliter
  EncodingFilter
  EncodingFilter
  no description
  com.sun..blueprints.encodingfilter.web.EncodingFilter

 
  encoding
  UTF-8
 


<!-- Encoding Filter Declaration End --&gt
<!-- Signon Filter Declaration Start --&gt
  //2.第二個Filter
  SignOnFilter
  SignOnFilter
  no description
  com.sun.j2ee.blueprints.signon.web.SignOnFilter

<!-- Signon Filter Declaration End --&gt

<!-- Encoding Filter Map Start--&gt
//第一個Filter對應
  EncodingFilter
  /*

<!-- Encoding Filter Mapping End --&gt
<!-- Signon Filter Mapping Start--&gt
//第二個Filter對應
  SignOnFilter
  /*

<!-- Signon Filter Mapping End --&gt

<!-- ComponentManager Listener --&gt
//3.第一個Listerner

com.sun.j2ee.blueprints.petstore.controller.web.PetstoreComponentManager


<!-- SignOn Attribute Listener --&gt
//4.第二個Listerner

com.sun.j2ee.blueprints.petstore.controller.web.SignOnNotifier



Filter與Listener是2.3增加的功能,Filter可以在接受使用者的Request之後,做一些檢查處理,若沒問題則把使用者要求的Response丟回給使用者,反之則丟回預設的處理畫面,最常使用的情況就是登入,在網頁應用系統中有些功能是必須登入才能使用,過去的做法我們會將登入檢查寫在這些個別功能上,如此會造成登入檢查若要修正,則必須逐支修改,造成時間浪費,運用Filter,我們可將登入檢查程式與其它程式獨立,日後容易維護。Listener則是增加對Context,Session生命週期的控制,例如我們能夠在Session初始化時,將所需使用的資料一起產生,並將Reference存入Session,Seesion關閉時可順便將相關資源移除,如此資源集中控管,容易維護。

Encoding Filter
它的程式程式碼位置在
Petstore_homesrccomponentsencodingfiltersrccomsunj2eeblueprintsencodingfilterwebEncodingFilter.java
,它會再讀取web.xml(位置在Petstore_homesrcappspetstoresrcdocrootWEB-INFweb.xml)中的引數,決定編碼方式再將其設入Request中:

web.xml
<!-- Encoding Filter Declaration Start --&gt
 
  EncodingFilter
  EncodingFilter
  no description
  com.sun.j2ee.blueprints.encodingfilter.web.EncodingFilter

  //設定編碼方式引數
  encoding
  UTF-8
 

 


EncodingFilter.java
public class EncodingFilter implements Filter {

  private FilterConfig config = null;
  // default to ASCII
  private String targetEncoding = "ASCII";

  //初始化時讀取引數
  public void init(FilterConfig config) throws ServletException {
  this.config = config;
  this.targetEncoding = config.getInitParameter("encoding");
  }

  public void destroy() {
  config = null;
  targetEncoding = null;
  }
  //將編碼方式引數存入reqeust,結束此Filter
  public void doFilter(ServletRequest srequest, ServletResponse  sresponse,
    FilterChain chain)
  throws IOException, ServletException {

  HttpServletRequest request = (HttpServletRequest)srequest;
  request.setCharacterEncoding(targetEncoding);
  // move on to the next
  chain.doFilter(srequest,sresponse);
  }
}

筆者覺得這個Filter滿有用的,可以單獨運用在傳統+JavaBeans的Web Application,在3.2.x,預設編碼是UTF-8,所以我們處理中文時,必須自行在程式中處理,在存入前須將中文字編碼由Unicode轉成Big5;到了Tomcat3.3.x,Tomcat會從OS取得預設編碼方式,所以我們不必再自行處理;到了Tomcat4.0.x之後,情況又回到與Tomcat3.2.x一樣,筆者開發Web Application就碰到這個情況,歷經三個版本的Tomcat,程式改過來改過去的,盡做些白工,後來筆者改運用這個Filter,就不需在存入資料庫前須將中文字編碼由Unicode轉成Big5。

ComponentManager Listener

第二個Filter主要掌管登入,與本次主題有密切關係,為使流程順暢,所以稍後再說明。我們來看第一個Listener,它的主要功能是作為服務連結提供者,當使用者進入本系統時,應用伺服器(Application Sever)會在Session產生時會建置儲存服務連結容器,並將其置入Session,待後續程式服務產生後使用。

DefaultComponentManager.java,原始碼在
Petstore_homesrcwafsrccontrollercomsunj2eeblueprintswafcontrollerweb

public void sessionCreated(HttpSessionEvent se) {
  HttpSession session = se.getSession();
  //產生服務連結容器
  sl = ServiceLocator.getInstance();
  //將本Listener存入Session
  session.setAttribute(WebKeys.COMPONENT_MANAGER, this);
}

ServiceLacator.java,原始碼在
Petstore_homesrccomponentsservicelocatorsrccomsunj2eeblueprintsservicelocatorweb
,此類別寫法滿特別的,大家又可多學一種技巧,追縱前輩原始碼,除了能夠了解各種功能如何設計之外,程式程式碼分解(Refactor)、撰寫風格、設計技巧,對我們來說都是很重要的收穫!ServiceLacator基本上它是一個及資源連結服務提供者,且它的服務不因使用者不同而有所改變,所以它使用static寫法來達到這個效果。

public class ServiceLocator {

  private InitialContext ic;
  private Map cache; //used to hold references to EJBHomes/JMS Res for
          //re-use

  private static ServiceLocator me;
  //初次宣告此時會初始化動作
  static {
  try {
  me = new ServiceLocator();
  } catch(ServiceLocatorException se) {
  System.err.println(se);
  se.printStackTrace(System.err);
  }
  }
  //建構子,產生JNDI連結及同步化HashMap容器
  private ServiceLocator() throws ServiceLocatorException  {
  try {
  ic = new InitialContext();
  cache = Collections.synchronizedMap(new HashMap());
  } catch (NamingException ne) {
  throw new ServiceLocatorException(ne);
  } catch (Exception e) {
  throw new ServiceLocatorException(e);
  }
  }

  //將本物件參考傳出
  static public ServiceLocator getInstance() {
  return me;
  }

以下略過…
}

上述ServiceLocator建構子中運用Collections的Static Method(靜態函式)synchronizedMap()來產生HashMap容器,依J2SDK 說法,透過此函式可產生出具同步化及執行緒的HashMap容器。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-993964/,如需轉載,請註明出處,否則將追究法律責任。

相關文章