1. 程式人生 > >struts2工作機制詳解

struts2工作機制詳解

讀者如果曾經學習過Struts1.x或者有過Struts1.x的開發經驗,那麼千萬不要想當然地以為這一章可以跳過。實際上Struts1.x與Struts2並無我們想象的血緣關係。雖然Struts2的開發小組極力保留Struts1.x的習慣,但因為Struts2的核心設計完全改變,從思想到設計到工作流程,都有了很大的不同。

  Struts2是Struts社群和WebWork社群的共同成果,我們甚至可以說,Struts2是WebWork的升級版,他採用的正是WebWork的核心,所以,Struts2並不是一個不成熟的產品,相反,構建在WebWork基礎之上的Struts2是一個執行穩定、效能優異、設計成熟的WEB框架。

  本章主要對Struts的原始碼進行分析,因為Struts2與WebWork的關係如此密不可分,因此,讀者需要下載xwork的原始碼,訪問http://www.opensymphony.com/xwork/download.action即可自行下載。

  下載的Struts2原始碼檔案是一個名叫struts-2.1.0-src.zip的壓縮包,裡面的目錄和檔案非常多,讀者可以定位到struts-2.1.0-src"struts-2.0.10"src"core"src"main"java目錄下檢視Struts2的原始檔,如圖14所示。

 Struts2的工作原理 - uzoice - 專心致志

  (圖14)

  主要的包和類

  Struts2框架的正常執行,除了佔核心地位的xwork的支援以外,Struts2本身也提供了許多類,這些類被分門別類組織到不同的包中。從原始碼中發現,基本上每一個Struts2類都訪問了WebWork提供的功能,從而也可以看出Struts2與WebWork千絲萬縷的聯絡。但無論如何,Struts2的核心功能比如將請求委託給哪個Action處理都是由xwork完成的,Struts2只是在WebWork的基礎上做了適當的簡化、加強和封裝,並少量保留Struts1.x中的習慣。

  以下是對各包的簡要說明:

包名 說明
org.apache.struts2. components 該包封裝檢視元件,Struts2在檢視元件上有了很大加強,不僅增加了元件的屬性個數,更新增了幾個非常有用的元件,如updownselect、doubleselect、datetimepicker、token、tree等。

  另外,Struts2視覺化檢視元件開始支援主題(theme),預設情況下,使用自帶的預設主題,如果要自定義頁面效果,需要將元件的theme屬性設定為simple。

org.apache.struts2. config 該包定義與配置相關的介面和類。實際上,工程中的xml和properties檔案的讀取和解析都是由WebWork完成的,Struts只做了少量的工作。
org.apache.struts2.dispatcher Struts2的核心包,最重要的類都放在該包中。
org.apache.struts2.impl 該包只定義了3個類,他們是StrutsActionProxy、StrutsActionProxyFactory、StrutsObjectFactory,這三個類都是對xwork的擴充套件。
org.apache.struts2.interceptor 定義內建的截攔器。
org.apache.struts2.util 實用包。
org.apache.struts2.validators 只定義了一個類:DWRValidator。
org.apache.struts2.views 提供freemarker、jsp、velocity等不同型別的頁面呈現。

  下表是對一些重要類的說明:

類名 說明
org.apache.struts2.dispatcher. Dispatcher 該類有兩個作用:

  1、初始化

  2、呼叫指定的Action的execute()方法。

org.apache.struts2.dispatcher. FilterDispatcher 這是一個過濾器。文件中已明確說明,如果沒有經驗,配置時請將url-pattern的值設成/*。

  該類有四個作用:

  1、執行Action

  2、清理ActionContext,避免記憶體洩漏

  3、處理靜態內容(Serving static content)

  4、為請求啟動xwork’s的截攔器鏈。

com.opensymphony.xwork2. ActionProxy Action的代理介面。
com.opensymphony.xwork2. ctionProxyFactory 生產ActionProxy的工廠。
com.opensymphony.xwork2.ActionInvocation 負責呼叫Action和截攔器。
com.opensymphony.xwork2.config.providers. XmlConfigurationProvider 負責Struts2的配置檔案的解析。
Struts2的工作機制3.1Struts2體系結構圖

  Strut2的體系結構如圖15所示:

 Struts2的工作原理 - uzoice - 專心致志

  (圖15)

  3.2Struts2的工作機制

  從圖15可以看出,一個請求在Struts2框架中的處理大概分為以下幾個步驟:

  1、客戶端初始化一個指向Servlet容器(例如Tomcat)的請求;

  2、這個請求經過一系列的過濾器(Filter)(這些過濾器中有一個叫做ActionContextCleanUp的可選過濾器,這個過濾器對於Struts2和其他框架的整合很有幫助,例如:SiteMesh Plugin);

  3、接著FilterDispatcher被呼叫,FilterDispatcher詢問ActionMapper來決定這個請求是否需要呼叫某個Action;

  4、如果ActionMapper決定需要呼叫某個Action,FilterDispatcher把請求的處理交給ActionProxy;

  5、ActionProxy通過Configuration Manager詢問框架的配置檔案,找到需要呼叫的Action類;

  6、ActionProxy建立一個ActionInvocation的例項。

  7、ActionInvocation例項使用命名模式來呼叫,在呼叫Action的過程前後,涉及到相關攔截器(Intercepter)的呼叫。

  8、一旦Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果。返回結果通常是(但不總是,也可能是另外的一個Action鏈)一個需要被表示的JSP或者FreeMarker的模版。在表示的過程中可以使用Struts2 框架中繼承的標籤。在這個過程中需要涉及到ActionMapper。

  注:以上步驟參考至網上,具體網址已忘記。在此表示感謝!

  3.3Struts2原始碼分析 

  和Struts1.x不同,Struts2的啟動是通過FilterDispatcher過濾器實現的。下面是該過濾器在web.xml檔案中的配置:

  程式碼清單6:web.xml(擷取)

  <filter>

    <filter-name>struts2</filter-name>

    <filter-class>

      org.apache.struts2.dispatcher.FilterDispatcher

    </filter-class>

  </filter>

  <filter-mapping>

    <filter-name>struts2</filter-name>

    <url-pattern>/*</url-pattern>

  </filter-mapping>

  Struts2建議,在對Struts2的配置尚不熟悉的情況下,將url-pattern配置為/*,這樣該過濾器將截攔所有請求。

  實際上,FilterDispatcher除了實現Filter介面以外,還實現了StrutsStatics介面,繼承程式碼如下:

  程式碼清單7:FilterDispatcher結構

publicclass FilterDispatcher implements StrutsStatics, Filter {

}

  StrutsStatics並沒有定義業務方法,只定義了若干個常量。Struts2對常用的介面進行了重新封裝,比如HttpServletRequest、HttpServletResponse、HttpServletContext等。 以下是StrutsStatics的定義:

  程式碼清單8:StrutsStatics.java

publicinterface StrutsStatics {

  /**

   *ConstantfortheHTTPrequestobject.

   */

  publicstaticfinal String HTTP_REQUEST = "com.opensymphony.xwork2.dispatcher.HttpServletRequest";

  /**

   *ConstantfortheHTTPresponseobject.

   */

  publicstaticfinal String HTTP_RESPONSE = "com.opensymphony.xwork2.dispatcher.HttpServletResponse";

  /**

   *ConstantforanHTTPrequest dispatcher}.

   */

  publicstaticfinal String SERVLET_DISPATCHER = "com.opensymphony.xwork2.dispatcher.ServletDispatcher";

  /**

   *Constantfortheservlet context}object.

   */

  publicstaticfinal String SERVLET_CONTEXT = "com.opensymphony.xwork2.dispatcher.ServletContext";

  /**

   *ConstantfortheJSPpage context}.

   */

publicstaticfinal String PAGE_CONTEXT = "com.opensymphony.xwork2.dispatcher.PageContext";

  /**ConstantforthePortletContextobject*/

  publicstaticfinal String STRUTS_PORTLET_CONTEXT = "struts.portlet.context";

}

  容器啟動後,FilterDispatcher被例項化,呼叫init(FilterConfig filterConfig)方法。該方法建立Dispatcher類的物件,並且將FilterDispatcher配置的初始化引數傳到物件中(詳情請參考程式碼清單10),並負責Action的執行。然後得到引數packages,值得注意的是,還有另外三個固定的包和該引數進行拼接,分別是org.apache.struts2.static、template、和org.apache.struts2.interceptor.debugging,中間用空格隔開,經過解析將包名變成路徑後儲存到一個名叫pathPrefixes的陣列中,這些目錄中的檔案會被自動搜尋。

  程式碼清單9:FilterDispatcher.init()方法

  publicvoid init(FilterConfig filterConfig) throws ServletException {

    this.filterConfig = filterConfig;   

    dispatcher = createDispatcher(filterConfig);

    dispatcher.init();   

    String param = filterConfig.getInitParameter("packages");

    String packages = "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";

    if (param != null) {

      packages = param + " " + packages;

    }

    this.pathPrefixes = parse(packages);

}

  程式碼清單10:FilterDispatcher.createDispatcher()方法

  protected Dispatcher createDispatcher(FilterConfig filterConfig) {

    Map<String,String> params = new HashMap<String,String>();

    for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements(); ) {

      String name = (String) e.nextElement();

      String value = filterConfig.getInitParameter(name);

      params.put(name, value);

    }

    returnnew Dispatcher(filterConfig.getServletContext(), params);

  }

  當用戶向Struts2傳送請求時,FilterDispatcher的doFilter()方法自動呼叫,這個方法非常關鍵。首先,Struts2對請求物件進行重新包裝,此次包裝根據請求內容的型別不同,返回不同的物件,如果為multipart/form-data型別,則返回MultiPartRequestWrapper型別的物件,該物件服務於檔案上傳,否則返回StrutsRequestWrapper型別的物件,MultiPartRequestWrapper是StrutsRequestWrapper的子類,而這兩個類都是HttpServletRequest介面的實現。包裝請求物件如程式碼清單11所示:

  程式碼清單11:FilterDispatcher.prepareDispatcherAndWrapRequest()方法

protectedHttpServletRequest prepareDispatcherAndWrapRequest(

    HttpServletRequest request,

    HttpServletResponse response) throws ServletException {

    Dispatcher du = Dispatcher.getInstance();

    if (du == null) {

      Dispatcher.setInstance(dispatcher);     

      dispatcher.prepare(request, response);

    } else {

      dispatcher = du;

    }    

    try {

      request = dispatcher.wrapRequest(request, getServletContext());

    } catch (IOException e) {

      String message = "Could not wrap servlet request with MultipartRequestWrapper!";

      LOG.error(message, e);

      thrownew ServletException(message, e);

    }

    return request;

}

  request物件重新包裝後,通過ActionMapper的getMapping()方法得到請求的Action,Action的配置資訊儲存在ActionMapping物件中,該語句如下:mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());。下面是ActionMapping介面的實現類DefaultActionMapper的getMapping()方法的原始碼:

  程式碼清單12:DefaultActionMapper.getMapping()方法

  public ActionMapping getMapping(HttpServletRequest request,

      ConfigurationManager configManager) {

    ActionMapping mapping = new ActionMapping();

    String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction!method

    uri = dropExtension(uri);//刪除副檔名,預設副檔名為action,在程式碼中的定義是List extensions = new ArrayList() {{ add("action");}};

    if (uri == null) {

      returnnull;

    }

    parseNameAndNamespace(uri, mapping, configManager);//從uri變數中解析出Action的name和namespace

    handleSpecialParameters(request, mapping);//將請求引數中的重複項去掉

    //如果Action的name沒有解析出來,直接返回

    if (mapping.getName() == null) {

      returnnull;

    }

    //下面處理形如testAction!method格式的請求路徑

    if (allowDynamicMethodCalls) {

      // handle "name!method" convention.

      String name = mapping.getName();

      int exclamation = name.lastIndexOf("!");//!是Action名稱和方法名的分隔符

      if (exclamation != -1) {

        mapping.setName(name.substring(0, exclamation));//提取左邊為name

        mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method

      }

    }

    return mapping;

  }

  該程式碼的活動圖如下:

 Struts2的工作原理 - uzoice - 專心致志

  (圖16)

  從程式碼中看出,getMapping()方法返回ActionMapping型別的物件,該物件包含三個引數:Action的name、namespace和要呼叫的方法method。

  如果getMapping()方法返回ActionMapping物件為null,則FilterDispatcher認為使用者請求不是Action,自然另當別論,FilterDispatcher會做一件非常有意思的事:如果請求以/struts開頭,會自動查詢在web.xml檔案中配置的packages初始化引數,就像下面這樣(注意粗斜體部分):

  程式碼清單13:web.xml(部分)

  <filter>

    <filter-name>struts2</filter-name>

    <filter-class>

      org.apache.struts2.dispatcher.FilterDispatcher

    </filter-class>

    <init-param>

      <param-name>packages</param-name>

      <param-value>com.lizanhong.action</param-value>

    </init-param>

  </filter>

  FilterDispatcher會將com.lizanhong.action包下的檔案當作靜態資源處理,即直接在頁面上顯示檔案內容,不過會忽略副檔名為class的檔案。比如在com.lizanhong.action包下有一個aaa.txt的文字檔案,其內容為“中華人民共和國”,訪問http://localhost:8081/Struts2Demo/struts/aaa.txt時會有如圖17的輸出:

 Struts2的工作原理 - uzoice - 專心致志

  (圖17)

  查詢靜態資源的原始碼如清單14:

  程式碼清單14:FilterDispatcher.findStaticResource()方法

  protectedvoid findStaticResource(String name, HttpServletRequest request, HttpServletResponse response) throws IOException {

    if (!name.endsWith(".class")) {//忽略class檔案

      //遍歷packages引數

      for (String pathPrefix : pathPrefixes) {

        InputStream is = findInputStream(name, pathPrefix);//讀取請求檔案流

        if (is != null) {

          ……(省略部分程式碼)

          // set the content-type header

          String contentType = getContentType(name);//讀取內容型別

          if (contentType != null) {

            response.setContentType(contentType);//重新設定內容型別

          }

         ……(省略部分程式碼)

          try {

           //將讀取到的檔案流以每次複製4096個位元組的方式迴圈輸出

            copy(is, response.getOutputStream());

          } finally {

            is.close();

          }

          return;

        }

      }

    }

  }

  如果使用者請求的資源不是以/struts開頭——可能是.jsp檔案,也可能是.html檔案,則通過過濾器鏈繼續往下傳送,直到到達請求的資源為止。

  如果getMapping()方法返回有效的ActionMapping物件,則被認為正在請求某個Action,將呼叫Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關鍵所在。上述過程的原始碼如清單15所示。

  程式碼清單15:FilterDispatcher.doFilter()方法

  publicvoid doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;

    HttpServletResponse response = (HttpServletResponse) res;

    ServletContext servletContext = getServletContext();

    String timerKey = "FilterDispatcher_doFilter: ";

    try {

      UtilTimerStack.push(timerKey);

      request = prepareDispatcherAndWrapRequest(request, response);//重新包裝request

      ActionMapping mapping;

      try {

        mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());//得到儲存Action資訊的ActionMapping物件

      } catch (Exception ex) {

        ……(省略部分程式碼)

        return;

      }

      if (mapping == null) {//如果mapping為null,則認為不是請求Action資源

         String resourcePath = RequestUtils.getServletPath(request);

        if ("".equals(resourcePath) && null != request.getPathInfo()) {

          resourcePath = request.getPathInfo();

        }

       //如果請求的資源以/struts開頭,則當作靜態資源處理

        if (serveStatic && resourcePath.startsWith("/struts")) {

          String name = resourcePath.substring("/struts".length());

          findStaticResource(name, request, response);

        } else {

          //否則,過濾器鏈繼續往下傳遞

          chain.doFilter(request, response);

        }

        // The framework did its job here

        return;

      }

      //如果請求的資源是Action,則呼叫serviceAction方法。

      dispatcher.serviceAction(request, response, servletContext, mapping);

    } finally {

      try {

        ActionContextCleanUp.cleanUp(req);

      } finally {

        UtilTimerStack.pop(timerKey);

      }

    }

  }

  這段程式碼的活動圖如圖18所示: Struts2的工作原理 - uzoice - 專心致志

  (圖18)

  在Dispatcher.serviceAction()方法中,先載入Struts2的配置檔案,如果沒有人為配置,則預設載入struts-default.xml、struts-plugin.xml和struts.xml,並且將配置資訊儲存在形如com.opensymphony.xwork2.config.entities.XxxxConfig的類中。

  類com.opensymphony.xwork2.config.providers.XmlConfigurationProvider負責配置檔案的讀取和解析, addAction()方法負責讀取<action>標籤,並將資料儲存在ActionConfig中;addResultTypes()方法負責將<result-type>標籤轉化為ResultTypeConfig物件;loadInterceptors()方法負責將<interceptor>標籤轉化為InterceptorConfi物件;loadInterceptorStack()方法負責將<interceptor-ref>標籤轉化為InterceptorStackConfig物件;loadInterceptorStacks()方法負責將<interceptor-stack>標籤轉化成InterceptorStackConfig物件。而上面的方法最終會被addPackage()方法呼叫,將所讀取到的資料彙集到PackageConfig物件中,細節請參考程式碼清單16。

  程式碼清單16:XmlConfigurationProvider.addPackage()方法

  protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {

    PackageConfig newPackage = buildPackageContext(packageElement);

    if (newPackage.isNeedsRefresh()) {

      return newPackage;

    }

    if (LOG.isDebugEnabled()) {

      LOG.debug("Loaded " + newPackage);

    }

    // add result types (and default result) to this package

    addResultTypes(newPackage, packageElement);

    // load the interceptors and interceptor stacks for this package

    loadInterceptors(newPackage, packageElement);

    // load the default interceptor reference for this package

    loadDefaultInterceptorRef(newPackage, packageElement);

    // load the default class ref for this package

    loadDefaultClassRef(newPackage, packageElement);

    // load the global result list for this package

    loadGlobalResults(newPackage, packageElement);

    // load the global exception handler list for this package

    loadGlobalExceptionMappings(newPackage, packageElement);

    // get actions

    NodeList actionList = packageElement.getElementsByTagName("action");

    for (int i = 0; i < actionList.getLength(); i++) {

      Element actionElement = (Element) actionList.item(i);

      addAction(actionElement, newPackage);

    }

    // load the default action reference for this package

    loadDefaultActionRef(newPackage, packageElement);

    configuration.addPackageConfig(newPackage.getName(), newPackage);

    return newPackage;

  }

  活動圖如圖19所示: Struts2的工作原理 - uzoice - 專心致志

  (圖19)

  配置資訊載入完成後,建立一個Action的代理物件——ActionProxy引用,實際上對Action的呼叫正是通過ActionProxy實現的,而ActionProxy又由ActionProxyFactory建立,ActionProxyFactory是建立ActionProxy的工廠。

  配置資訊載入完成後,建立一個Action的代理物件——ActionProxy引用,實際上對Action的呼叫正是通過ActionProxy實現的,而ActionProxy又由ActionProxyFactory建立,ActionProxyFactory是建立ActionProxy的工廠。

  注:ActionProxy和ActionProxyFactory都是介面,他們的預設實現類分別是DefaultActionProxy和DefaultActionProxyFactory,位於com.opensymphony.xwork2包下。

  在這裡,我們絕對有必要介紹一下com.opensymphony.xwork2.DefaultActionInvocation類,該類是對ActionInvocation介面的預設實現,負責Action和截攔器的執行。

  在DefaultActionInvocation類中,定義了invoke()方法,該方法實現了截攔器的遞迴呼叫和執行Action的execute()方法。其中,遞迴呼叫截攔器的程式碼如清單17所示:

  程式碼清單17:呼叫截攔器,DefaultActionInvocation.invoke()方法的部分程式碼

    if (interceptors.hasNext()) {

       //從截攔器集合中取出當前的截攔器

        final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();

        UtilTimerStack.profile("interceptor: "+interceptor.getName(),

           new UtilTimerStack.ProfilingBlock<String>() {

             public String doProfiling() throws Exception {

              //執行截攔器(Interceptor)介面中定義的intercept方法

               resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);

              returnnull;

             }

        });

      }

  從程式碼中似乎看不到截攔器的遞迴呼叫,其實是否遞迴完全取決於程式設計師對程式的控制,先來看一下Interceptor介面的定義:

  程式碼清單18:Interceptor.java

publicinterface Interceptor extends Serializable {

  void destroy();

  void init();

  String intercept(ActionInvocation invocation) throws Exception;

}

  所有的截攔器必須實現intercept方法,而該方法的引數恰恰又是ActionInvocation,所以,如果在intercept方法中呼叫invocation.invoke(),程式碼清單17會再次執行,從Action的Intercepor列表中找到下一個截攔器,依此遞迴。下面是一個自定義截攔器示例:

  程式碼清單19:CustomIntercepter.java

publicclass CustomIntercepter extends AbstractInterceptor {

  @Override

  public String intercept(ActionInvocation actionInvocation) throws Exception

  {

    actionInvocation.invoke();

    return"李贊紅";

  }

}

  截攔器的呼叫活動圖如圖20所示:

 Struts2的工作原理 - uzoice - 專心致志

  (圖20)

  如果截攔器全部執行完畢,則呼叫invokeActionOnly()方法執行Action,invokeActionOnly()方法基本沒做什麼工作,只調用了invokeAction()方法。

  為了執行Action,必須先建立該物件,該工作在DefaultActionInvocation的構造方法中呼叫init()方法早早完成。呼叫過程是:DefaultActionInvocation()->init()->createAction()。建立Action的程式碼如下:

  程式碼清單20:DefaultActionInvocation.createAction()方法

  protectedvoid createAction(Map contextMap) {

    try {

      action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);

    } catch (InstantiationException e) {

    ……異常程式碼省略

    }

  }

  Action建立好後,輪到invokeAction()大顯身手了,該方法比較長,但關鍵語句實在很少,用心點看不會很難。

  程式碼清單20:DefaultActionInvocation.invokeAction()方法

protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {

  //獲取Action中定義的execute()方法名稱,實際上該方法是可以隨便定義的

    String methodName = proxy.getMethod();

    String timerKey = "invokeAction: "+proxy.getActionName();

    try {

      UtilTimerStack.push(timerKey);      

      Method method;

      try {

       //將方法名轉化成Method物件

        method = getAction().getClass().getMethod(methodName, new Class[0]);

      } catch (NoSuchMethodException e) {

        // hmm -- OK, try doXxx instead

        try {

         //如果Method出錯,則嘗試在方法名前加do,再轉成Method物件

          String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);

          method = getAction().getClass().getMethod(altMethodName, new Class[0]);

        } catch (NoSuchMethodException e1) {

          // throw the original one

          throw e;

        }

      }

      //執行方法

      Object methodResult = method.invoke(action, new Object[0]);

      //處理跳轉

    if (methodResult instanceof Result) {

        this.result = (Result) methodResult;

        returnnull;

      } else {

        return (String) methodResult;

      }

    } catch (NoSuchMethodException e) {

       ……省略異常程式碼

    } finally {

      UtilTimerStack.pop(timerKey);

    }

  }

  剛才使用了一段插述,我們繼續回到ActionProxy類。

  我們說Action的呼叫是通過ActionProxy實現的,其實就是呼叫了ActionProxy.execute()方法,而該方法又呼叫了ActionInvocation.invoke()方法。歸根到底,最後呼叫的是DefaultActionInvocation.invokeAction()方法。

  以下是呼叫關係圖:

 Struts2的工作原理 - uzoice - 專心致志

  其中:

  Ø     ActionProxy:管理Action的生命週期,它是設定和執行Action的起始點。

  Ø     ActionInvocation:在ActionProxy層之下,它表示了Action的執行狀態。它持有Action例項和所有的Interceptor

  以下是serviceAction()方法的定義:

  程式碼清單21:Dispatcher.serviceAction()方法

    publicvoid serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,

               ActionMapping mapping) throws ServletException {

    Map<String, Object> extraContext = createContextMap(request, response, mapping, context);

    // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action

    ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

    if (stack != null) {

      extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));

    }

    String timerKey = "Handling request from Dispatcher";

    try {

      UtilTimerStack.push(timerKey);

      String namespace = mapping.getNamespace();

      String name = mapping.getName();

      String method = mapping.getMethod();

      Configuration config = configurationManager.getConfiguration();

      ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(

          namespace, name, extraContext, true, false);

      proxy.setMethod(method);

      request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

      // if the ActionMapping says to go straight to a result, do it!

      if (mapping.getResult() != null) {

        Result result = mapping.getResult();

        result.execute(proxy.getInvocation());

      } else {

        proxy.execute();

相關推薦

struts2工作機制

讀者如果曾經學習過Struts1.x或者有過Struts1.x的開發經驗,那麼千萬不要想當然地以為這一章可以跳過。實際上Struts1.x與Struts2並無我們想象的血緣關係。雖然Struts2的開發小組極力保留Struts1.x的習慣,但因為Struts2的核心設計完

JVM結構、GC工作機制

固定 本地方法棧 內存池 為什麽 water aml 並且 兩種 數據區 轉自:http://blog.csdn.net/tonytfjing/article/details/44278233 JVM結構、內存分配、垃圾回收算法、垃圾收集器。下面我們一一來看。 一、JVM結

JVM、Gc工作機制

相同 生命 棧幀 VM 每次 失效 劃分 地址 .com JVM主要包括四個部分: 類加載器(ClassLoad) 執行引擎 內存區: 本地方法接口:類似於jni調本地native方法 內存區包括四個部分: 1.方法區:包含了靜態變量、常量池、構造函數等

業余草分享面試題,JVM結構、GC工作機制

影響 根節點 tac 關註 共享 產生 我想 tar 效果 題外話:最近在應聘阿裏2015暑期實習,感觸頗多。機會總是留給有準備的人的,所以平常一定要註意知識的鞏固和積累。知識的深度也要有一定的理解,不比別人知道的多,公司幹嘛選你?關於JVM和GC,我相信學java的絕大部

MapReduce工作機制

memory 傳遞 等待 mapper 臨時文件 相等 water tsp 以及 1.MapTask工作機制整個Map階段流程大體如上圖所示。簡單概述:input File通過split被邏輯切分為多個split文件,通過Record按行讀取內容給map(用戶自己實現的)進

NIO元件Selector工作機制(上)

在使用Java進行相關網路程式的的設計時,出身C/C++的人,首先想到的框架就是多路複用,想到多路複用,Unix/Linux下馬上就能讓從想到select, poll, epoll系統呼叫。於是,在看到Java的NIO中的Selector類時必然會倍感親切。稍加查閱一下SDK手冊以及相關例程,不一會兒,一個

GC工作機制

一、JVM結構 根據《java虛擬機器規範》規定,JVM的基本結構一般如下圖所示: 從左圖可知,JVM主要包括四個部分: 1.類載入器(ClassLoader):在JVM啟動時或者在類執行時將需要的class載入到JVM中。(右圖表示了從java原始檔到JVM的整

TCP的工作機制

TCP的特點及其目的 為了通過IP資料報實現可靠性傳輸,需要考慮很多事情,例如資料的破壞、丟包、重複以及分片順序混亂等,TCP通過校驗和、序列號、確認應答、重發控制、連線管理以及視窗控制等機制等實現可靠性傳輸。 通過序列號與確認應答提高可靠性 在TCP

【Spark工作機制】 執行機制

Spark主要包括  排程與任務分配、I/O模組、通訊控制模組、容錯模組  、 Shuffle模組。 Spark 按照   ①應用  application  ②作業 job   ③ stage  ④ task   四個層次進行排程,採用經典的FIFO和FAIR等排程演

MapReduce工作機制(MapTask和ReduceTask)

MapTask:1.maptask0負責切片0 ,maptask1負責切片1,maptask2負責切片2。2.maptask0通過一個元件TextinputFormat讀切片0,這個元件封裝一個LineRecordReader,裡面有next方法,每調一次方法從切片0裡讀一行

【網路程式設計】深入理解TCP的工作機制

TCP的特點及其目的 為了通過IP資料報實現可靠性傳輸,需要考慮很多事情,例如資料的破壞、丟包、重複以及分片順序混亂等,TCP通過校驗和、序列號、確認應答、重發控制、連線管理以及視窗控制等機制等實現可靠性傳輸。 通過序列號與確認應答提高可靠性 在TCP中,當傳送端的

JVM 和 GC的工作機制

一些基礎的知識結構和底層的原理性的東西還是需要好好進行研究的,這樣就更有助於理解 JAVA 的很多知識;本文章是在檢視<<深入理解Java 虛擬機器>>後所得心得,希望對大家有所幫助,也歡迎技術大咖批評指教; 一、JVM結構、記憶體分配、垃圾回收演算

Hadoop框架:NameNode工作機制

本文原始碼:[GitHub·點這裡](https://github.com/cicadasmile/big-data-parent) || [GitEE·點這裡](https://gitee.com/cicadasmile/big-data-parent) # 一、儲存機制 ## 1、基礎描述 Nam

Hadoop框架:DataNode工作機制

本文原始碼:[GitHub·點這裡](https://github.com/cicadasmile/big-data-parent) || [GitEE·點這裡](https://gitee.com/cicadasmile/big-data-parent) # 一、工作機制 ## 1、基礎描述 ![]

中斷底半部機制工作佇列

工作佇列的使用方法和tasklet 非常相似,下面的程式碼用於定義一個工作佇列和一個底半部執行函式。 struct work_struct my_wq; /*定義一個工作佇列*/ void my_wq_func(unsigned long); /*定義一個處理函式*/ 通過INIT_W

Struts2之非同步呼叫機制

一、學習案例:通過在getXML.jsp頁面改變名稱,在不重新整理當前頁面的情況下修改頁面的資料。 二、案例分析:struts和ajax配合,主要是返回資料的定義。我們可以返回json和xml格式的資料。在此只演示xml方式。 a)格式xml資料,一是拼接字串,但是資料

lvs和keeplived的工作原理

lvs+keeplived的工作原理一、lvs的工作原理 使用集群的技術和liunx的操作系統實現一個高性能、高可用的服務器。可伸縮性、可靠性、很好的管理性。 特點:可伸縮網絡服務的幾種結構,它們都需要一個前端的負載調度器(或者多個進行主從備份)。我們先分析實現虛擬網絡服務的主要技術,指出IP負載均衡技術

Java反射機制

java 反射 反射機制 工廠模式 1反射機制是什麽反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。在面向對象的世界裏,萬事萬物皆對象.在ja

Java垃圾回收(GC)機制

nbsp 引用計數 維護 png 對象 最新 新的 com 前沿 垃圾回收算法有兩種,根據不同的虛擬機策略不同 1、引用計數法 2、可達性分析法 由於我們平常使用的hotspot虛擬機用的是第二種。 那哪些是可達的呢? 這個算法的基本思想是通過一系列稱為“GC Roots”

Java的內存回收機制

out 結果 int destroy pan 得出 ida public toc http://blog.csdn.net/mengern/article/details/38150431 Java中提供了垃圾強制回收機制的方法System.gc(),但是系統並不保證會立即