如何在項目實踐中擴充Struts

來源:互聯網
上載者:User
項目   我看到很多項目中,開發人員實現了自己的MVC架構,並不是因為他們想做同Struts根本不同的東西,而是因為他們並沒有意識到如何擴充Struts。開發自己的MVC架構可以獲得全部的控制權,但是這也意味著需要很多資源來實現它(人力物力),在緊張的排程下,有時候這是不可能的。   Struts不僅僅是一個強大的架構,同時它也是可擴充的。你可以以三種方式來擴充Struts。   1、PlugIn:如果你想在application startup或shutdown的時候做一些商務邏輯的話,那就建立你自己的PlugIn類。   2、RequestProcessor:如果你想在請求被處理的過程中某個時刻做一些商務邏輯的話,那麼建立你自己的RequestProcessor類。比如說,在每次請求執行之前,你可以擴充RequestProcessor來檢查使用者是否登陸了以及他是否有許可權去執行某個特定的action。   3、ActionServlet:如果你想在application startup和shutdown的時候以及請求被處理的時候做某些商務邏輯,你也可以擴張ActionServlet類。不過你應當在PlugIn和RequestProcessor都不能解決你的需求的時候來使用ActionServlet。   在這篇文章中,我們將使用一個Struts應用的樣本來示範如何使用這三種方式來擴充Struts。樣本程式的代碼可以從http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip下載。兩個擴充Struts成功的範例是Struts自身的Validation和Tiles架構。   我們假設你已經比較熟悉Struts架構並且知道如何使用它建立一個簡單的應用。如果你想知道更多關於Struts的內容,請參考官方首頁。   PlugIn      PlugIn是一個介面,你可以建立一個實現該介面的類,當application startup或shutdown的時候做些事情。   比方說,我建立了一個使用Hibernate作為持久層的web應用,我想當應用啟動的時候就初始化Hibernate,這樣子當我的web應用受到第一個請求的時候,Hibernate就已經是配置好的並且可用的。同時我們想當application關閉的時候關閉Hibernate。我們可以用一個Hibernate PlugIn來實現這個需求,通過如下的兩步:   1、建立一個類實現了PlugIn介面:   public class HibernatePlugIn implements PlugIn{   private String configFile;   // This method will be called at application shutdown time   public void destroy() {   System.out.println("Entering HibernatePlugIn.destroy()");   //Put hibernate cleanup code here   System.out.println("Exiting HibernatePlugIn.destroy()");   }   //This method will be called at application startup time   public void init(ActionServlet actionServlet, ModuleConfig config)   throws ServletException {    System.out.println("Entering HibernatePlugIn.init()");    System.out.println("value of init parameter " +    getConfigFile());    System.out.println("Exiting HibernatePlugIn.init()");   }   public String getConfigFile() {   return name;   }   public void setConfigFile(String string) {   configFile = string;   }   }   實現PlugIn介面的類必須完成兩個方法:init()和destroy()。當application startup的時候init()方法被調用,當shutdown的時候destroy()方法被調用。Struts還允許給你的PlugIn類傳遞初始化參數。為了傳遞參數,你必須在PlugIn類中為每一個參數建立JavaBean式的setter方法。在我們的HibernatePlugIn類中,我會把configFile的name作為參數傳進去,而不是寫入程式碼到程式中。   2、在struts-config.xml中添加如下的代碼來通告Struts有新的PlugIn:   <struts-config>   ...   <!-- Message Resources -->   <message-resources parameter= "sample1.resources.ApplicationResources"/>   <!-- Declare your plugins -->   <plug-in className="com.sample.util.HibernatePlugIn">   <set-property property="configFile" value="/hibernate.cfg.xml"/>   </plug-in>   </struts-config>   屬性className是實現了PlugIn介面的類的全限定名。對於每一個初始化參數,可以使用<set-property>元素傳遞參數。在我們的例子中,我要把config檔案的名字傳進去,所以使用了一個帶有設定檔路徑的<set-property>。   Struts的Tiles和Validator架構都使用PlugIn來讀取設定檔進行初始化。另外兩件PlugIn可以幫你做到的事情是:   ·如果你的application依賴於某些設定檔,那麼你可以在PlugIn類中檢查它們是否可用,如果停用話拋出一個ServletException,這樣就可以使ActionServlet變為不可用。   ·PlugIn介面的init()方法是你可以改變ModuleConfig的最後機會,ModuleConfig是一組靜態配置資訊的集合,用來描述基於Struts模組。Struts將會在所有PlugIn處理完後釋放ModuleConfig。    Request是如何被處理的   ActionServlet是Struts架構中唯一的Servlet,它負責處理所有request。無論何時接收到一個request,它都會先嘗試為當前的request尋找一個sub-application。一旦一個sub-application被找到,ActionServlet就會為那個sub-application建立一個RequestProcessor對象,調用這個對象的process()方法並把HttpServletRequest和HttpServletResponse對象傳入。   RequestProcessor.process()就是大部分request被處理的地方。process()方法使用了Template Method模式實現,其中有很多獨立的方法來執行請求處理的每一步驟,這些方法將會在process方法中依次被調用。比如,將會有一個獨立的方法用來尋找當前request對應的ActionForm類,一個方法來檢查目前使用者是否有執行action mapping所必須的許可權。這些給與我們極大的靈活性。在發布的Struts包中有一個RequestProcessor類提供了請求處理每一步驟的預設實現。這就意味著你可以僅僅重寫你感興趣的方法,其它的使用預設的實現。舉例來說,預設地Struts調用request.isUserInRole()來檢查使用者是否有許可權執行當前的ActionMapping;這時如果你想通過查詢資料庫來實現,你所要做的就是重寫processRoles()方法,通過查詢出的使用者是否擁有必須的許可權來返回true或false。   首先我們將會看到預設情況下,process()方法是如何?的,然後我將會詳細解釋預設的RequestProcessor類中的每一個方法,這樣你就可以決定哪一部分是你想要改變的。   public void process(HttpServletRequest request,HttpServletResponse response)   throws IOException, ServletException {   // Wrap multipart requests with a special wrapper   request = processMultipart(request);   // Identify the path component we will   // use to select a mapping   String path = processPath(request, response);   if (path == null) {   return;   }   if (log.isDebugEnabled()) {   log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'");   }   // Select a Locale for the current user if requested   processLocale(request, response);   // Set the content type and no-caching headers   // if requested   processContent(request, response);   processNoCache(request, response);   // General purpose preprocessing hook   if (!processPreprocess(request, response)) {   return;   }   // Identify the mapping for this request   ActionMapping mapping =   processMapping(request, response, path);   if (mapping == null) {   return;   }   // Check for any role required to perform this action   if (!processRoles(request, response, mapping)) {   return;   }   // Process any ActionForm bean related to this request   ActionForm form = processActionForm(request, response, mapping);   processPopulate(request, response, form, mapping);   if (!processValidate(request, response, form, mapping)) {   return;   }   // Process a forward or include specified by this mapping   if (!processForward(request, response, mapping)) {   return;   }   if (!processInclude(request, response, mapping)) {   return;   }   // Create or acquire the Action instance to   // process this request   Action action =   processActionCreate(request, response, mapping);   if (action == null) {   return;   }   // Call the Action instance itself   ActionForward forward = processActionPerform(request, response,action, form, mapping);   // Process the returned ActionForward instance   processForwardConfig(request, response, forward);   }   1、processMutipart():在這個方法中,Struts將會讀取request來檢查request的contentType是否是multipart/form-data。如果是的話,將會解析request並且將之封裝到HttpServletRequest中。當你建立了一個HTML FORM用來提交資料,那麼request的contentType預設就是application/x-www-form-urlencoded。但是如果你的form使用了file類型的input控制項允許使用者上傳檔案的話,你就必須將contentType改為multipart/form-data。如果是這樣的情況,你就不能再通過getParameter()來擷取使用者提交的資料;你必須將request作為一個InputStream來讀取,並且自己解析它來獲得參數值。   2、processPath():在這個方法中,Struts將會讀取request的URI,來確定路徑元素,這個元素是用來擷取ActionMappint元素。   3、processLocale():在這個方法中,Struts將會為當前request取得Locale,如果配置過的話,還可以將這個對象作為HttpSession中org.apache.struts.action.LOCALE屬性的值而儲存。作為這個方法的副作用,HttpSession將會被建立,如果你不想建立的話,你可以在ControllerConfig中將locale屬性設為false,在struts-config.xml中象如下這樣:   <controller>   <set-property property="locale" value="false"/>   </controller>   4、processContent():通過調用response.setContentType()來為response設定contentType。這個方法首先會嘗試從struts-config.xml配置中得到contentType。預設情況下使用text/html。如果想覆蓋它,可以象如下這樣:   <controller>   <set-property property="contentType" value="text/plain"/>   </controller>   5、processNoCache():如果配置是no-cache,Struts將會為每個response設定下面三個headers:   requested in struts config.xml   response.setHeader("Pragma", "No-cache");   response.setHeader("Cache-Control", "no-cache");   response.setDateHeader("Expires", 1);   如果你想設定no-cache header,在struts-config.xml中加入下面資訊:   <controller>   <set-property property="noCache" value="true"/>   </controller>   6、processPreprocess():這個方法為預先處理提供一個hook,可以在子類中覆蓋它。它的預設實現沒有作任何事情,總是返回true。返回false的話將會終止當前請求的處理。   7、processMapping():這個方法將會用路徑資訊得到一個ActionMapping對象。也就是struts-config.xml檔案中的<action>元素:   <action path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request">   <forward name="sucess" path="/sucessPage.do"/>   <forward name="failure" path="/failurePage.do"/>   </action>   ActionMapping元素包含了Action類的名稱和處理請求使用的ActionForm等等資訊。它還包含當前ActionMapping配置的ActionForwards資訊。   8、processRoles():Struts web應用提供了一個授權方案。也就是說,一旦一個使用者登入了容器,struts的processRoles()方法將會通過調用request.isUserInRole(),來檢查他是否有必須的角色來運行一個給定的ActionMapping。   <action path="/addUser" roles="administrator"/>   假設你有一個AddUserAction並且你只想讓administrator能夠增加新的user。你所要做的就是給你的AddUserAction元素增加一個role屬性,這個屬性的值為administrator。這樣,在運行AddUserAction之前,這個方法會確保使用者擁有administraotr的角色。   9、processActionForm():每一個ActionMapping都一個相應的ActionForm類。當Struts處理一個ActionMapping的時候,它將會從<action>元素的name屬性中找出對應的ActionForm類的名稱。   <form-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm">   <form-property name="firstName" type="java.lang.String"/>   <form-property name="lastName" type="java.lang.String"/>   </form-bean>   在我們的例子中,它會先在request scope中檢查是否有一個org.apache.struts.action.DynaActionForm類的對象存在。如果有它將會使用這個對象,如果沒有它將會建立一個新的對象並把它設定在request scope。   10、processPopulate():在這個方法中,Struts將會用相匹配的request參數裝配ActionForm的執行個體變數。   11、processValidate():Struts將會調用你的ActionForm類的validate方法。如果你從validate()返回ActionErrors,它將會將user重新導向到<action>元素的input屬性指定的頁面。   12、processForward()和processInclude():在這些方法中,Struts將會檢查<action>元素的forward或include屬性,如果找到了,將會把forward或include請求放置到配置的頁面中。   <action forward="/Login.jsp" path="/loginInput"/>   <action include="/Login.jsp" path="/loginInput"/>   你可以從這些方法的名字上猜測它們的不同:processForward()最終調用RequestDispatcher.forward(),而processInclude()調用RequestDispatcher.include()。如果你同時配置了forward和include屬性,它將會總是調用forward,因為forward先被處理。   13、processActionCreate():這個方法從<action>元素的type屬性中擷取獲得Action類的名字並且建立返回它的執行個體。在我們的例子中,它將會建立一個com.sample.NewContactAction類的執行個體。   14、processActionPerform():這個方法調用你的Action類的excute()方法,你的商務邏輯也就是在excute方法中。   15、processForwardConfig():你的Action類的excute()方法將會返回一個ActionForward對象,這個對象將指出哪個頁面是顯示給使用者的頁面。因此,Struts將會為那個頁面建立一個RequestDispatcher,並且調用RequestDispatcher.forward()。   上面的列表說明了預設的RequestProcessor實現在處理請求時每一步作的工作,以及執行的順序。正如你所看到的,RequestProcessor是非常靈活的,允許你通過設定<controller>元素的屬性來配置它。舉例來說,如果你的應用準備產生XML內容來代替HTML,你就可以通過設定controller元素的屬性來通知Struts這些情況。      建立你自己的RequestProcessor   通過上面,我們瞭解到了RequestProcessor的預設實現是如何工作的。現在我們要示範一個例子來說明如何定製你自己的RequestProcessor。為了展示建立使用者定製的RequestProcessor,我們將會讓我們的樣本實現下面兩個業務需求:   ·我們想建立一個ContactImageAction類,它將產生圖片而不是平常的HTML頁面。   ·在每個請求處理之前,我們都想通過檢查session中的userName屬性來確定使用者是否已經登陸。如果那個屬性沒有找到,我們會把使用者重新導向到登陸頁面。   我們將分兩步實現這些業務需求。   1、建立你的CustomRequestProcessor類,它將繼承自RequestProcessor類,如下:   public class CustomRequestProcessor   extends RequestProcessor {   protected boolean processPreprocess (   HttpServletRequest request,HttpServletResponse response) {    HttpSession session = request.getSession(false);    //If user is trying to access login page    // then don't check    if( request.getServletPath().equals("/loginInput.do")   || request.getServletPath().equals("/login.do") )     return true;    //Check if userName attribute is there is session.    //If so, it means user has allready logged in    if( session != null && session.getAttribute("userName") != null)     return true;    else{     try{      //If no redirect user to login Page      request.getRequestDispatcher("/Login.jsp").forward(request,response);     }catch(Exception ex){     }    }    return false;   }   protected void processContent(HttpServletRequest request,   HttpServletResponse response) {   //Check if user is requesting ContactImageAction   // if yes then set image/gif as content type   if( request.getServletPath().equals("/contactimage.do")){    response.setContentType("image/gif");    return;   }    super.processContent(request, response);   }    }   在CustomRequestProcessor類的processPreprocess方法中,我們檢查session的userName屬性,如果沒有找到,就將使用者重新導向到登陸頁面。   對於產生圖片作為輸出的需求,我們必須覆蓋processContent方法,首先檢查請求是否是/contactimage路徑。如果是的話,我們就會將contentType設定為image/gif;否則設定為text/html。   2、在你的struts-config.xml檔案的<action-mappint>元素之後加入下面的文字,告訴Struts CustomRequestProcessor應當被用作RequestProcessor類:   <controller>   <set-property property="processorClass"value="com.sample.util.CustomRequestProcessor"/>   </controller>   請注意,當你只有很少的action類需要產生非text/html類型的輸出時,你覆寫processContent()方法是OK的。如果不是這樣子的話,你應該建立一個Struts的子應用來處理請求產生圖片的Action,並為它們將contentType設定為image/gif。   Struts的Tiles架構就是使用它自己的RequestProcessor來裝飾Struts的輸出。   ActionServlet   如果你查看你的Struts web應用的web.xml,你會看到這樣的文字:   <web-app >   <servlet>   <servlet-name>action=</servlet-name>   <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>   <!-- All your init-params go here-->   </servlet>   <servlet-mapping>   <servlet-name>action</servlet-name>   <url-pattern>*.do</url-pattern>   </servlet-mapping>   </web-app >   這意味著ActionServlet負責處理你所有Struts的請求。你可以建立一個ActionServlet的子類,當應用啟動,關閉,每個請求的時候做一些特定的事情。但是在繼承ActionServlet類之前,你應該盡量建立一個PlugIn或RequestProcessor去解決你的問題。在Servlet1.1之前,Tiles架構是基於ActionServlet來修飾產生的響應。但是從1.1之後,它開始使用TilesRequestProcessor類。   總結   決定開發你自己的MVC架構是一個非常大的決定,你必須要考慮開發和維護架構代碼所花費的時間和資源。Struts是一個非常強大和穩定的架構,你可以修改它來滿足你絕大多數的業務需求。   但另一方面,也不要草率地做出擴充Struts的決定。如果你在RequestProcessor中寫了一些效能比較低的代碼,它將會在每次請求時執行,因而降低你整個應用的效率。而且還是有一些情況,開發自己的MVC架構要比擴充Struts好。

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。