Struts 學習筆記2 -ActionServlet深入探討

來源:互聯網
上載者:User

java.lang.Object
  |
  +--javax.servlet.GenericServlet
        |
        +--javax.servlet.http.HttpServlet
              |
              +--org.apache.struts.action.ActionServlet

Struts提供了一個預設版本的ActionServlet類,你可以繼承這個類,覆蓋其中的一些方法來達到你的特殊處理的需要。ActionServlet繼承與javax.servlet.http.HttpServlet,所以在本質上它和一個普通的servlet沒有區別,你完全可以把它當做一個servlet來看待,只是在其中完成的功能不同罷了。ActionServlet主要完成如下功能:

將一個來自用戶端的URI映射到一個相應的Action類

  • 如果是這個Action類是第一次被調用,那麼執行個體化一個並放入緩衝
  • 如果在設定檔(struts-config.xml)中指定了相應的ActionForm,那麼從Request中抓取資料填充FormBean
  • 調用這個Action類的perform()方法,傳入ActionMapping的一個引用,對應的ActionForm、以及由容器傳給ActionServlet的HttpServletRequest、HttpServletResponse對象。

確省版本的ActionServlet會從設定檔web.xml中讀取如下初始化參數:

  • application
    應用使用的資源套件(resources bundle)的基類
  • factory
    用於建立應用的MessageResources對象的MessageResourcesFactory的類名。確省是org.apache.struts.util.PropertyMessageResourcesFactory。
  • config
    Struts的設定檔,確省是/WEB-INF/struts-config.xml。注意這兒是與應用Context關聯的相對路徑。
  • content
    定義了確省的內容類型和編碼格式,它會被自動地被設定到每個response中,如果JSP/Servlet中沒有明確的設定。確省是text/html。
  • debug
    調試資訊的層級。預設為0,比當前層級高的調試資訊會被log到記錄檔中。
  • detail
    與debug的作用類似,只是這個detail是initMapping()時專用的。調試資訊會被列印到System.out,而不是記錄檔。
  • formBean
    ActionFormBean的實作類別,確省為org.apache.struts.action.ActionFormBean
  • forward
    應用中使用的ActionForward類,確省是org.apache.struts.action.ActionForward。
  • locale
    指定了確省使用的Locale對象。設為true,當得到一個session時,會自動在session中儲存一個以Action.LOCALE_KEY標示的Locale對象,如果session中還沒有與Action.LOCALE_KEY綁定的Locale對象。
  • mapping
    應用中使用的ActionMapping類,確省是org.apache.struts.action.ActionMapping。
  • multipartClass
    檔案上傳使用的MutipartRequestHandler的實作類別。確省為org.apache.struts.upload.DiskMultipartRequestHandler
  • nocache
    如果設為true,那麼ActionServlet會自動在每個到用戶端的響應中添加nocache的HTML頭,這樣用戶端就不會對應用中的頁面進行緩衝。確省為false
  • null
    如果設定為true,那麼應用在得到一個未定義的message資源時,會返回null,而不是返回一個錯誤資訊。確省是true。
  • maxFileSize
    檔案上傳的大小上限,確省為250M
  • bufferSize
    檔案上傳時的緩衝區的大小,確省為4M
  • tempDir
    設定用於上傳時的臨時目錄。工作目錄會作為一個Servlet環境(Context)的屬性提供。
  • validate
    Are we using the new configuration file format?確省為true。
  • validating

在解析配置XML檔案是是否進行有效性的驗證。確省為true

ActionServlet中應用了命令設計模式。

一個Servlet在由容器產生時,首先會調用init()方法進行初始化,在接到一個HTTP請求時,調用相應的方法進行處理;比如GET請求調用doGet()方法,POST請求調用doPost()方法。所以首先看看ActionServlet的init()方法,你就會很清楚為什麼ActionServlet可以完成這些功能了。

init()

在它的init()方法中,ActionServlet依次調用如下protected的方法完成初始化:

  • initActions() -     大家可能還曾有這個疑問:Struts為什麼可以找到一個請求URI對應的action類呢?答案就在這兒,ActionServlet有一個actions屬性,類型為org.apache.struts.util.FastHashMap,用於儲存以類的全名為key的已執行個體化的Action類。在init()時首先調用的就是initActions()方法,在這個方法中只是簡單的清除map中的所有的名值對,
    1.  
    2.         synchronized (actions) {
    3.             actions.setFast(false);
    4.             actions.clear();
    5.             actions.setFast(true);
    6.         }

    首先把actions設為slow模式,這時對FastHashMap的訪問是線程同步的,然後清除actions中的所有的已存在的名/值對,最後再把actions的模式設為fast。由於FastHashMap是struts在java.util.HashMap的基礎上的一個擴充類,是為了適應多線程、並且對HashMap的訪問大部分是唯讀特殊環境的需要。大家知道java.util.HashMap是非安全執行緒的,所以HashMap一般適用於單線程環境下。org.apache.struts.FastHashMap就是繼承於java.util.HashMap,在其中添加多線程的支援產生的。在fast模式下的工作方式是這樣的:讀取是非線程同步的;寫入時首先複製當前map,然後在這個複製上做寫入操做,完成後用這個修改後的複製版本替換原來的map。那麼在什麼時候會把Actions類添加到這個map中呢?我們已經提到了struts是動態產生Action類的執行個體的,在每次ActionServlet接收到一個GET或POST的HTTP請求時,會在這個map中尋找對應的Action類的執行個體,如果不存在,那麼就執行個體化一個,並放入map中。可見這個actions屬性起到了對Action類執行個體的緩衝的作用。

  • initInternal() -     初始化ActionServlet內部使用的資源套件MessageResources,使用MessageResources.getMessageResources(internalName)得到    internalName為"org.apache.struts.action.ActionResources"對應的ActionResources.properties檔案。這個資源套件主要用於ActionServlet處理過程中的用到的提示資訊,這兒不展開討論。
  • initDebug() -     從web.xml中讀取本應用的debug層級參數getServletConfig().getInitParameter("debug"),然後賦給debug屬性。
  • initApplication()-    初始化應用資源套件,並放置入ServletContext中。
    1.  
    2.         String factory =getServletConfig().getInitParameter(“factory”);    
    3.         String oldFacory = MessageResourcesFactory.getFactoryClass();
    4.         if (factory !=null)
    5.                 MessageResourcesFactory.setFactoryClass(factory);
    6.         String value = getServletConfig().getInitParameter("application");
    7.         MessageResourcesFactory factoryObject =
    8.                 MessageResourcesFactory.createFactory();
    9.         application = factoryObject.createResources(value);
    10.         MessageResourcesFactory.setFactory(oldFactory); 
    11.         getServletContext().setAttribute(Action.MESSAGES_KEY, application);

    說明:文中引用的代碼片斷可能會省略了一些例外檢查等非主線的內容,敬請注意。

    首先從設定檔中讀取factory參數,如果這個參數不為空白,那麼就在MessageResourcesFactory中使用這個指定的Factory類;否則,使用預設的工廠類org.apche.struts.util.PropertyMessageResourceFactory。然後調用MessageResourcesFactory的靜態createFactory()方法,產生一個具體的MessageResourceFactory對象(注意:MessageResourcesFactory是抽象類別)。這樣就可以調用這個具體的MessageResourceFactory的createResource()方法得到設定檔(web.xml)中定義的資源檔了。
    上面的application物件類型為MessageResources。在web.xml中在配置ActionServlet時可以指定一個特定的工廠類。不能直接MessageResourcesFactory的createResources()方法,因為這個方法是abstract的。建立factoryObject的過程如下:

    1.     
    2.         MessageResourceFactory factoryObject=
    3.                 MessageResourcesFactory.createFactory();
    4.         application = factoryObject.createResources(value);

    <li>initMapping() -    為應用初始化mapping資訊ActionServlet有一個protected的屬性:mapping,封裝了一個ActionMapping的對象集合,以便於管理、尋找ActionMapping。mappings是org.apache.struts.action.ActionMappings類的執行個體。主要有兩個方法:addMapping(ActionMapping mapping)和findMapping(String path)。ActionMapping也是使用上面提到的org.apache.struts.util.FastHashMap類來儲存所有的ActionMapping對象。

    1.  
    2.         mappings.setServlet(this);
    3.         ……
    4.         // Initialize the name of our ActionFormBean implementation class
    5.         value = getServletConfig().getInitParameter("formBean");
    6.         if (value != null)
    7.             formBeanClass = value;
    8.     
    9.         // Initialize the name of our ActionForward implementation class
    10.         value = getServletConfig().getInitParameter("forward");
    11.         if (value != null)
    12.                 forwardClass = value;
    13.  
    14.         // Initialize the name of our ActionMapping implementation class
    15.         value = getServletConfig().getInitParameter("mapping");
    16.         if (value != null)
    17.                 mappingClass = value;

    在initMapping()中,首先連結mappings對象到本servlet執行個體。其實這句話的作用很簡單,在ActionMappings中會有一個ActionServlet類型的屬性,這個屬性就界定了這個ActionMappings對象所屬的ActionServlet。Struts的實現比較靈活,其中的ActionFormBean、ActionForward、ActionMapping類你完全可以使用自己實現的子類,來定製Struts的工作方式。上面的代碼就從設定檔(web.xml)中讀取formBean、forward、mapping參數,這些參數就是你定製的ActionFormBean、ActionForward、ActionMapping類名。

    1.  
    2.         // Initialize the context-relative path to our configuration resources
    3.         value = getServletConfig().getInitParameter("config");
    4.         if (value != null)
    5.                 config = value;
    6.                 // Acquire an input stream to our configuration resource
    7.         InputStream input = getServletContext().getResourceAsStream(config);
    8.         Digester digester = null;
    9.                 digester = initDigester(detail);
    10.         try {
    11.                 formBeans.setFast(false);
    12.                 forwards.setFast(false);
    13.                 mappings.setFast(false);
    14.                 digester.parse(input);
    15.                 mappings.setFast(true);
    16.                 forwards.setFast(true);
    17.                 formBeans.setFast(true);
    18.         } catch (SAXException e) {
    19.                 throw new ServletException
    20.         (internal.getMessage("configParse", config), e);
    21.         } finally {
    22.                 input.close();
    23.         }

    從web.xml讀取Struts的設定檔的位置。使用org.apache.struts.digester.Digester解析config參數標示的設定檔,通常為“/WEB-INF/struts-config.xml”,解析出所有的data-source、form-bean、action-mapping、forward。從上面的程式片斷看到,Digester僅僅調用了一個parse()方法,那麼,Digester是怎樣把解析struts-config.xml檔案並把解析的結果form-bean等資訊儲存到屬性變數formBeans等中的呢?你可以注意到在調用digester.parse(InputStream)之前,首先調用了initDigester()方法:

    1.  
    2.         Digester digester = new Digester();
    3.         digester.push(this);
    4.         digester.addObjectCreate("struts-config/action-mappings/action",
    5.                                  mappingClass, "className");
    6.         digester.addSetProperties("struts-config/action-mappings/action");
    7.         digester.addSetNext("struts-config/action-mappings/action",
    8.                             "addMapping",
    9.                             "org.apache.struts.action.ActionMapping");
    10.  
    11.         digester.addSetProperty
    12.             ("struts-config/action-mappings/action/set-property",
    13.              "property", "value");

    在這個方法中首先產生一個Digester對象,然後設定解析的規則和回調,如果你對XML、SAX不是很熟,這兒不必糾纏太深。要注意的是addSetNext()方法,設定了每一個要解析元素的Set Next回調方法,而這個方法就是由digester解析器的父提供的。上面的片斷中的“addMapping”就是ActionServlet本身定義的一個方法,將由Digester回調。Digester就是籍此把解析出的每一個FormBean、ActionForward、ActionMapping等儲存到屬性變數formBeans、forwards、mappings等中的。

  • initUpload() -    初始化有關Upload的一些參數,比如:bufferSize、tempDir。
  • initDataSource() -取出在initMapping()中從設定檔中讀取的每一個DataSource,設定LogWriter,如果為GenericDataSource的執行個體,則開啟資料來源。然後,把每個dataSource放入Context中。
    dataSource.setLogWriter(scw);
    ((GenericDataSource)dataSource).open();
    getServletContext().setAttribute(key,dataSource);
  • initOther() -        設定其它尚未初始化的的參數(content、locale、nocache),並發布formBeans、forwards、mappings到Context:
    getServletContext().setAttribute(Action.FORM_BEANS_KEY, formBeans);
    getServletContext().setAttribute(Action.FORWARDS_KEY, forwards);
    getServletContext().setAttribute(Action.MAPPINGS_KEY, mappings);
  • initServlet() -    初始化Controller Servlet的Servlet Mapping。這兒也使用了Digester工具,掃描web.xml所有的<web-app/servlet-mapping>,尋找servlet-name與當前Servlet相同的mapping,置入Context。代碼如下;
    1.  
    2.         Digester digester = new Digester();
    3.         digester.push(this);
    4.         digester.setDebug(debug);
    5.         digester.setValidating(validating);
    6.         digester.addCallMethod(“web-appservlet-mapping”,“addServletMapping”, 2);
    7.         digester.addCallParm(“web-appservlet-mappingservlet-name”, 0);
    8.         digester.addCallParm(“web-appservlet-mappingurl-pattern”, 1);
    9.         InputStream is = getServletContext().getResourceAsStream(“/WEB-INFweb.xml”);
    10.         digester.parse(is); 
    11.         getServletContext().setAttribute(Action.SERVLET_KEY,servletMapping);

聯繫我們

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

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

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.