程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 軟件體系架構模式在J2EE中的應用

軟件體系架構模式在J2EE中的應用

編輯:關於JAVA

本文介紹了軟件體系架構產生的背景和架構模式的基本理論。重點介紹管道與過濾器體系架構模式的結構,實現,優缺點等,然後以J2EE的Servlet Filter為例進行剖析它是怎樣應用該架構模式的,最後簡單闡述了在其它J2ee應用中(Jboss和Axis)的實踐。

軟件體系架構

1、軟件體系架構產生背景

在經歷60年代的軟件危機之後,使人們開始重視軟件工程的研究。來自不同應用領域的軟件專家總結了大量的有價值的知識。 當初,人們把軟件設計的重點放在數據結構和算法的選擇上,如Knuth提出了數據結構+算法=程序。 但是隨著軟件系統規模越來越大、越來越復雜,使軟件系統的架構越來越重要。軟件危機的程度日益加劇,現有的軟件工程方法對此顯得力不從心。對於大規模的復雜軟件系統來說,軟件體系架構比起對程序的算法和數據結構的選擇已經變得明顯重要得多。在此種背景下,人們認識到軟件體系架構的重要性,並認為對軟件體系架構系統、深入的研究將會成為提高軟件生產效率和解決軟件危機的最有希望的途徑。 這時對軟件體系架構的研究如雨後春筍般,出現了百家爭鳴的現象,如Rational公司提出了"以架構為中心"的統一軟件開發過程(RUP)。

2、軟件體系架構模式

軟件設計的一個核心問題是能否使用重復的體系架構,即能否達到體系架構級的軟件重用。也就是說,能否在不同的軟件系統中,使用同一體系架構。基於這個目的,許多學者們開始研究和實踐軟件體系架構的模式問題。在<Pattern-Oriented Software Architecture (面向模式的軟件體系架構) >中首次提出了8種體系結構模式: 層(Layers)、管道和過濾器(Pipes and Filters) 、黑板(Black board )、代理者(Broker)、模型-視圖-控制器(Model-View-Controller)、表示-抽象-控制(Presentation-Abstraction-Control)、微核(Microkernel)、映像(Reflection)。

J2EE體系架構

目前,J2EE技術已經成為企業級應用的首選平台,基於J2EE技術構建的軟件系統越來越多。J2EE代表著先進的軟件體系架構思想,許多軟件體系架構模式在J2ee中均被廣泛應用,從本文起陸續介紹各架構模式在J2EE中的應用。

管道與過濾器

1、概述

管道和過濾器(Pipes and Filters)體系架構模式是為處理數據流的系統提供的一種模式。它是由過濾器和管道組成的.每個處理步驟都被封裝在一個過濾器組件中,數據通過相鄰過濾器之間的管道進行傳輸。每個過濾器可以單獨修改,功能單一,並且它們之間的順序可以進行配置。下圖是管道/過濾器模式的示意圖。一個典型的管道/過濾器體系結構的例子是以Unix shell編寫的程序。Unix既提供一種符號,以連接各組成部分(Unix的進程),又提供某種進程運行時機制以實現管道。另一個著名的例子是傳統的編譯器。傳統的編譯器一直被認為是一種管道系統,在該系統中,一個階段(包括詞法分析、語法分析、語義分析和代碼生成)的輸出是另一個階段的輸入。

軟件體系架構模式在J2EE中的應用

2、問題

假如你正在開發一個必須處理或轉換輸入數據流的系統。把這樣的系統作為單個組件實現是不容易的,這有幾個原因:系統必須由幾個開發人員同時進行協作開發,整個系統任務自然就被分解為幾個處理階段,而且需求很容易變動。因此你就要通過替換或重新排序處理步驟來為將來的靈活性作規劃。通過加入這樣的靈活性,采用現有處理組件構建是可以辦到的。系統的設計尤其是處理步驟的內部連接,必須考慮以下因素:

未來系統的升級通過替換某些處理步驟,或重組步驟.

不同的語境中小的處理步驟要比大的組件更易於重用。

不相連的處理步驟不可共享信息。

存在不同的輸入數據源,

可以用多種方式輸出或存放最終結果。

3、解決方案與結構

管道和過濾器體系架構模式把系統任務分成為幾個獨立的處理步驟。這些步驟采用通過系統的數據流連接。一個步驟的輸出是下一個步驟的輸入。每個處理步驟由一個過濾器組件實現,它處理或者轉化數據,並且系統的輸入可以是多種數據源。

這種體系架構模式具有許多特性,如下:

過濾器是獨立運行的部件.也就是除了輸入和輸出外,每個過濾器不受任何其他過濾器運行的影響.在設計上,過濾器之間不共享任何狀態信息。

獨立性還表現在它對其處理的上游和下游連接的過濾器是"無知"的.它的設計和使用不對與其連接的任何過濾器施加限制,唯一關心的是其輸入數據的,然後進行加工處理,最後產生數據輸出。

4、非軟件描述 基於各種流體工作的系統,普遍都采用由輸送管道連接起來的處理結構.例如我們冬天見到的供暖系統中,處理器包括加熱器,過濾器,調節閥,流量表等,每個處理器都有流體的入口和出口,它們通過各種管道連接在一起形成了整個系統.這樣的結構在城市的自來水系統也可以看到。見下圖:

軟件體系架構模式在J2EE中的應用

5、優點與缺點

5.1 優點

通過使用過濾器交換增加了靈活性

通過重組增加了靈活性

過濾器組件的重用

流水線的快速原型

並行處理提高效率

5.2 缺點

共享狀態信息或者昂貴或者不靈活

數據轉換額外開銷。

錯誤處理

Servlet2.3 Filter

1、Servlet Filter概述

凡是開發過J2EE的web application的人員都知道,經常需要處理以下幾種情況:

訪問特定資源(Web 頁、JSP 頁、servlet)時的身份認證

應用程序級的訪問資源的審核和記錄

應用程序范圍內對資源的加密訪問,它建立在定制的加密方案基礎上

對被訪問資源的及時轉換, 包括從 servlet 和 JSP 的動態輸出

在servlet2.3之前這些功能處理是很難實現的,但是Java Servlet 2.3 規范新增了不少激動人心的功能,其中之一便是過濾器(Filter),其實這就是我們所說的管道和過濾器體系架構在J2EE中的應用實踐. 通過使用該模式使得Web Application開發者能夠在請求到達Web資源之前截取請求,在處理請求之後修改應答。其結構圖如下:

軟件體系架構模式在J2EE中的應用

一個執行過濾器的Java 類必須實現javax.servlet.Filter 接口。這一接口含有三個方法:

init(FilterConfig):這是容器所調用的初始化方法。它保證了在第一次 doFilter() 調用前由容器調用。它能獲取在 web.xml 文件中指定的filter初始化參數。

doFilter(ServletRequest, ServletResponse, FilterChain):這是一個完成過濾行為的方法。它同樣是上一個過濾器調用的方法。引入的 FilterChain 對象提供了後續過濾器所要調用的信息。

destroy():容器在銷毀過濾器實例前,doFilter()中的所有活動都被該實例終止後,調用該方法。

軟件體系架構模式在J2EE中的應用

2、Filter鏈介紹

所有過濾器都服從調用的過濾器鏈,並通過定義明確的接口得到執行。WebApplication可以指定許多過濾器來完成相關的工作.那麼它們就組成一個過濾器鏈來完成相應的工作.其結構如下圖:

軟件體系架構模式在J2EE中的應用

3、例子

3.1 簡單filter

在PetStore1.3.1中的就存在兩個Filter過濾器.其中一個過濾器,完成字符集的編碼的轉化,如大家經常遇到的漢字編碼問題,你只需配置為GBK即可.它從Web.xml之中讀取這些參數的配置信息,然後進行編碼的轉化.另一個是安全校驗Fliter,它負責進行安全檢查哪些頁面可以進行,哪些不可。它們組成一個Filter鏈,結構圖和實現代碼如下:

軟件體系架構模式在J2EE中的應用

public class EncodingFilter implements Filter {
private FilterConfig config = null;
// default to ASCII
private String targetEncoding = "ASCII";
public void init(FilterConfig config) throws ServletException {
this.targetEncoding = config.getInitParameter("encoding");
}
//在過濾器中實現字符集編碼轉化
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);
}
public void destroy() {
……………..
}
}
public class SignOnFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
this.config = config;
URL protectedResourcesURL = null;
try {
protectedResourcesURL = config.getServletContext().getResource("/WEB-INF/signon-config.xml");
...............
} catch (java.net.MalformedURLException ex) {
System.out.println("SignonFilter: malformed URL exception: " + ex);
}
}
public void destroy() {
config = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
........
}
}

容器通過 Web 應用程序中的配置描述符 web.xml 文件解析過濾器配置信息。有兩個新的標記與過濾器相關:<filter> 和 <filter-mapping>。<filter> 標記是一個過濾器定義,它必定有一個 <filter- name> 和 <filter-class> 子元素。<filter-name> 子元素給出了一個與過濾器實例相關的名字。<filter-class> 指定了由容器載入的實現類。您能隨意地包含一個 <init-param> 子元素為過濾器實例提供初始化參數。<filter-mapping> 標記代表了一個過濾器的映射,指定了過濾器會對其產生作用的 URL 的子集。

<!-- Encoding Filter Declaration Start -->
<filter>
<filter-name>EncodingFilter</filter-name>
<display-name>Encoding Filter</display-name>
<description>no description</description>
<filter-class>com.sun.j2ee.blueprints.encodingfilter.web.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- Encoding Filter Declaration End -->
<!-- Signon Filter Declaration Start -->
<filter>
<filter-name>SignOnFilter</filter-name>
<display-name>SignOn Filter</display-name>
<description>no description</description>
<filter-class>com.sun.j2ee.blueprints.signon.web.SignOnFilter</filter-class>
</filter>
<!-- Signon Filter Declaration End -->
<!-- Encoding Filter Mapping Start-->
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Encoding Filter Mapping End -->
<!-- Signon Filter Mapping Start-->
<filter-mapping>
<filter-name>SignOnFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Signon Filter Mapping End -->

3.2 復雜的filter

上面是petstore的例子,演示了通過Fliter修改字符編碼和安全認證的功能。下面提供一個示例演示通過修改返回數據(通過過濾器把response的字符串變成大寫)。

public class UCaseResponse extends HttpServletResponseWrapper {
public UCaseResponse(HttpServletResponse response) {
super(response);
}
public PrintWriter getWriter() throws IOException {
return new UCaseWriter(super.getWriter());
}
}
public class UCaseWriter extends PrintWriter {
public UCaseWriter(Writer out) {
super(out);
}
public void write(int c) {
super.write(Character.toUpperCase( (char) c));
}
public void write(char buf[], int off, int len) {
for (int i = 0;i < len;i++) {
write(buf[off + i]);
}
}
public void write(String s, int off, int len) {
for (int i = 0;i < len;i++) {
write(s.charAt(off + i));
}
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) {
try {
filterChain.doFilter(request, new UCaseResponse((HttpServletResponse)(response)));
}catch(Exception sx) {
filterConfig.getServletContext().log(sx.getMessage());
}

該示例使用HttpServletResponseWrapper技術,它是對HttpServletResponse的包裝,其實就是裝飾(decorate)設計模式的應用.這個例子能夠工作的關鍵是UCaseResponse和UCaseWriter類,它實現了對每個要輸出的字符都轉成了大寫後再寫入實際的輸出流的功能。

4、體系架構的實現

實現一個管道和過濾器一般要注意以下幾個方面:

把系統任務分成一系列處理階段。

根據管道和過濾器的設計方案,必須把系統處理的任務分割成相應獨立的任務,如日志,數據轉化,安全認證等。這樣每個階段僅依賴其前一階段的輸出。通過數據流將所有階段相連起來。並且你可以進行替換每個步驟,或者可以調整它們之間的順序,以產生新的結果.如petstore中的編碼轉化Filter和安全Filter,分成兩個獨立的處理階段。

定義沿每個管道傳輸的數據格式。

我們知道每個過濾器,定義一個統一格式以獲得最大的靈活性,因為它使過濾器的重組變得容易。如:每個過濾器的方法是doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)它們的參數是必須相同的。

決定如何實現每個管道連接

Filter過濾器的連接是推得方式來實現的.前一個過濾器主動的調用filterChain.doFilter(request, response);來實現轉向下一個過濾器。

設計和實現過濾器

設計每個Filter具有獨立的功能,如編碼轉化,安全校驗,等功能.並且每個Fliter都應該在實現javax.servlet.Filter接口。

建立處理流水線

過濾器的部署是在Web.xml中進行配置,描述過濾器的實現類以及它們的map關系,來確定它們的順序。

其他應用實例

1、JBOSS

如果大家對EJB了解,應該知道客戶調用EJB實際上並不是直接引用EJB實例(ejb instance).它通過容器來截獲客戶端的請求,然後按照EJB描述符做些許多相應的工作如,安全校驗,事務的處理,線程並發處理等.這樣就可以使開發人員僅關心自己的業務邏輯,而不需對復雜的基礎服務進行實現.使開發人員從繁瑣的工作中解脫出來.集中精力處理自己的業務邏輯,它的結構圖如下:

軟件體系架構模式在J2EE中的應用

筆者有幸閱讀了JBOSS的源碼,分析了Jboss的EJB容器的實現. EJB的容器通過許多攔截器(Inteceptor)來實現,每個攔截器處理一定的功能,一個處理結束後轉發給下一個攔截器,最後一個攔截器才把真正調用EJB的實例.其中它的EntityBean 容器的攔截器的結構如下:

軟件體系架構模式在J2EE中的應用

我們看其中的log攔截器

public class LogInterceptor extends AbstractInterceptor{
public Object invoke(Invocation invocation)
throws Exception
{
boolean trace = log.isTraceEnabled();
// Log call details
if (callLogging)
{
......進行log的處理
}
//處理結束,把請求轉發給下一個攔截器
return getNext().invoke(invocation);
}

這些攔截器配置在standardjboss.xml文件中,如下:

<container-interceptors>
<interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.TxInterceptorCMT</interceptor>
<interceptor metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.EntityLockInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.EntityInstanceInterceptor</interceptor>
<interceptor>org.jboss.resource.connectionmanager.CachedConnectionInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.EntitySynchronizationInterceptor</interceptor>
<interceptor>org.jboss.ejb.plugins.cmp.jdbc.JDBCRelationInterceptor</interceptor>
</container-interceptors>

這就是Jboss容器架構最巧妙的地方,最初這個架構就是由天才少年Rickard Oberg提出的.其實這個架構就應用我們討論的管道和過濾器模式,其實每個攔截器就是一個過濾器,使用該模式給Jboss帶來了如下的好處:

使系統的架構更容易理解,因為每個過濾器完成單一的功能。

使系統更加模塊化,有利於系統的模塊重用和擴展,如果系統想增加某種功能只需增加一個實現Interceptor接口的攔截器,然後配置在standardjboss.xml文件中即可。

使系統的容易進行錯誤處理,如果在一個攔截器中發現錯誤(error)或者異常(exception),只需返回即可.

2、AXIS

無獨有偶,同樣在Axis上也應用了管道和過濾器模式.Aixs是apache開源的webservice實現服務器。簡單的說,axis就是處理Message,它首先截獲客戶端的請求,然後轉發到真正的實現業務邏輯上處理客戶端的請求,在這之前經過一系列的handler處理.它的結構很像EJB容器.其實就是管道和過濾器模式的應用,Handler就是過濾器.它的處理順序主要考慮兩個方面一個是部署描述符(deployment configuration )另一個就是是客戶端還是服務器端。Handler處理的對象是MessageContext它的由3個重要的部分組成,一是一個request Message,一個是response message,還有許多屬性。

我們經研究源碼分析,在服務器端,有一個Transport Listener 它監聽客戶端的請求, 可以通過多種協議,一旦有客戶請求,它將按照協議的規范把數據解析生成生成一個Message對象,然後把它設置到MessageContext,然後調用一系列的Handler進行處理。

其結構圖如下:

軟件體系架構模式在J2EE中的應用

相關設計模式

在使用管道和過濾器模式時,一般會使用以下的GOF設計模式。

1、職責鏈模式(Chain Of Responsibility)

職責鏈設計模式的意圖就是使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。其實管道和過濾器模式就是職責鏈模式的抽象,把它應用到軟件體系架構中。

2、 命令模式(Command)

命令模式的意圖將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可撤消的操作。在管道和過濾器模式中每個過濾器一般使用命令模式,把請求封裝成一個命令進行處理。

3、裝飾模式(Decorator)

裝飾模式的意圖就是動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。

在管道和過濾器模式中,在每個過濾器中經常需要對請求進行動態的增加功能,或者修改請求的內容,這時一般會使用裝飾模式.如Servlet filter的javax.servlet.http.HttpServletRequestWrapper, javax.servlet.http.HttpServletResponseWrapper就是裝飾模式的應用.

總結

本文討論了管道和過濾器模式的解決方案,以及它的優缺點.然後以J2EE規范的Servlet Filter為例詳細介紹了怎樣應用該模式,同時簡單的介紹了在Jboss,Axis中的應用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved