Working Principle of Struts 2

Keywords: Struts Apache xml Java

Before learning struts 2, we need to understand the purpose of using struts 2. What benefits can it bring us?

design goal

The first goal of Struts design is to apply MVC pattern to web programming. The benefits of MVC mode are not mentioned here.

Technological superiority

Struts 2 has two technical advantages. First, all Struts 2 applications are based on client/server HTTP exchange protocol. JavaServlet API reveals. Java Servlet is only a small subset of Java API, so we can use powerful Java language to program in business logic.

The second is to provide a clear implementation of MVC, which includes many key components involved in processing all requests, such as interceptor, OGNL expression language, stack.


Because struts 2 has such goals and advantages, this is why we learn struts 2. Next, we will analyze the working principle of struts in depth.

Working principle

The working principle of Suruts 2 can be described in the following figure. Here we introduce the core content of each step step step step by step.


Processing a request in the Struts2 framework is roughly divided into the following steps

1. The client initializes a request to a Servlet container (such as Tomcat)

2. This request passes through a series of filters (one of which is an optional Filter called ActionContextCleanUp, which is useful for integrating Struts 2 and other frameworks, such as SiteMesh Plugin).

3. Then the FilterDispatcher is called, and the FilterDispatcher asks the Action Mapper to decide whether this request needs to call an Action.

FilterDispatcher is the core of the controller and the core of c control layer in mvc. Following is a rough analysis of the filter Dispatcher workflow and principles I understand: Filter Dispatcher initialization and core doFilter enabled

  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException ...{  
  2.         HttpServletRequest request = (HttpServletRequest) req;  
  3.         HttpServletResponse response = (HttpServletResponse) res;  
  4.         ServletContext servletContext = filterConfig.getServletContext();  
  5.         //HttpServletRequest and HttpServletResponse are processed here.   
  6.         DispatcherUtils du = DispatcherUtils.getInstance();  
  7.         du.prepare(request, response);//Make locale, encoding, and special request parameters settings just like the method name.
  8.         try ...{  
  9.             request = du.wrapRequest(request, servletContext);//Package the request
  10.         } catch (IOException e) ...{  
  11.             String message = "Could not wrap servlet request with MultipartRequestWrapper!";  
  12.             LOG.error(message, e);  
  13.             throw new ServletException(message, e);  
  14.         }  
  15.                 ActionMapperIF mapper = ActionMapperFactory.getMapper();//Get action mapper
  16.         ActionMapping mapping = mapper.getMapping(request);//mapping to get action
  17.         if (mapping == null) ...{  
  18.             // there is no action in this request, should we look for a static resource?  
  19.             String resourcePath = RequestUtils.getServletPath(request);  
  20.             if ("".equals(resourcePath) && null != request.getPathInfo()) ...{  
  21.                 resourcePath = request.getPathInfo();  
  22.             }  
  23.             if ("true".equals(Configuration.get(WebWorkConstants.WEBWORK_SERVE_STATIC_CONTENT))   
  24.                     && resourcePath.startsWith("/webwork")) ...{  
  25.                 String name = resourcePath.substring("/webwork".length());  
  26.                 findStaticResource(name, response);  
  27.             } else ...{  
  28.                 // this is a normal request, let it pass through  
  29.                 chain.doFilter(request, response);  
  30.             }  
  31.             // WW did its job here  
  32.             return;  
  33.         }  
  34.         Object o = null;  
  35.         try ...{  
  36.             //setupContainer(request);  
  37.             o = beforeActionInvocation(request, servletContext);  
  38. //The core method of the whole framework is analyzed below.
  39.             du.serviceAction(request, response, servletContext, mapping);  
  40.         } finally ...{  
  41.             afterActionInvocation(request, servletContext, o);  
  42.             ActionContext.setContext(null);  
  43.         }  
  44.     }  
  45. du.serviceAction(request, response, servletContext, mapping);  
  46. //This method asks whether an Action Mapper needs to call an Action to process the request. If the Action Mapper decides that an Action needs to be called, the FilterDispatcher handles the request to ActionProxy.
  47.    
  48. public void serviceAction(HttpServletRequest request, HttpServletResponse response, String namespace, String actionName, Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap) ...{   
  49.         HashMap extraContext = createContextMap(requestMap, parameterMap, sessionMap, applicationMap, request, response, getServletConfig());  //Instantiate the Map request and ask if ActionMapper needs to call an Action to process the request.
  50.         extraContext.put(SERVLET_DISPATCHER, this);   
  51.         OgnlValueStack stack = (OgnlValueStack) request.getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY);   
  52.         if (stack != null) ...{   
  53.             extraContext.put(ActionContext.VALUE_STACK,new OgnlValueStack(stack));   
  54.         }   
  55.         try ...{   
  56.             ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);   
  57. //Here actionName is parsed through two getAction Name channels. FilterDispatcher handles the request to ActionProxy. Here is Servlet Dispatcher's TODO:.
  58.             request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack());   
  59.             proxy.execute();   
  60.          //ActionProxy is executed through proxy mode
  61.             if (stack != null)...{   
  62.                 request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY,stack);   
  63.             }   
  64.         } catch (ConfigurationException e) ...{   
  65.             log.error("Could not find action", e);   
  66.             sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);   
  67.         } catch (Exception e) ...{   
  68.             log.error("Could not execute action", e);   
  69.             sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);   
  70.         }   
  71. }   


 

4. If ActionMapper decides that an Action needs to be invoked, FilterDispatcher handles the request to ActionProxy

5. ActionProxy queries the configuration file of the framework through Configuration Manager and finds the Action class that needs to be invoked. Here, we usually read from the struts.xml configuration.

6. ActionProxy creates an instance of ActionInvocation.

7. ActionInvocation instances are invoked using naming mode. Before and after the invocation of Action, the invocation of Intercepter is involved.

Let's look at how Action Invocation works:

ActionInvocation is the core of Action scheduling in Xworks. ActionInvocation is also responsible for the scheduling of Interceptor. ActionInvocation is an interface, and DefaultAction Invocation is the default implementation of ActionInvocation by Webwork.

Interceptor's scheduling process is roughly as follows:

1. When ActionInvocation is initialized, all Interceptor s associated with Action are loaded according to configuration.

2. Interceptor is executed when the Action implementation is invoked through the ActionInvocation.invoke method.

Interceptor separates many functions from our Action, greatly reduces the code of our Action, and the independent behavior has good reusability. Many of the functions of XWork and WebWork are implemented by Interceptor, which can be assembled in the configuration file and run before and after the action is executed in the order you specify.

Here, let's briefly introduce Interceptor

There are many interceptors in struts 2. In struts 2-core-2.1.6.jar, we can find that:

  1. <interceptors>  
  2.            <interceptor name="alias"class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>  
  3.            <interceptor name="autowiring"class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>  
  4.            <interceptor name="chain"class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>  
  5.            <interceptor name="conversionError"class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>  
  6.            <interceptor name="clearSession"class="org.apache.struts2.interceptor.ClearSessionInterceptor"/>  
  7.            <interceptor name="createSession"class="org.apache.struts2.interceptor.CreateSessionInterceptor"/>  
  8.            <interceptor name="debugging"class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor"/>  
  9.            <interceptor name="externalRef"class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>  
  10.            <interceptor name="execAndWait"class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>  
  11.            <interceptor name="exception"class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>  
  12.            <interceptor name="fileUpload"class="org.apache.struts2.interceptor.FileUploadInterceptor"/>  
  13.            <interceptor name="i18n"class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>  
  14.            <interceptor name="logger"class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>  
  15.            <interceptor name="modelDriven"class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>  
  16.            <interceptor name="scopedModelDriven"class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>  
  17.            <interceptor name="params"class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>  
  18.            <interceptor name="actionMappingParams"class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/>  
  19.            <interceptor name="prepare"class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>  
  20.            <interceptor name="staticParams"class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>  
  21.            <interceptor name="scope"class="org.apache.struts2.interceptor.ScopeInterceptor"/>  
  22.            <interceptor name="servletConfig"class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>  
  23.            <interceptor name="sessionAutowiring"class="org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor"/>  
  24.            <interceptor name="timer"class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>  
  25.            <interceptor name="token"class="org.apache.struts2.interceptor.TokenInterceptor"/>  
  26.            <interceptor name="tokenSession"class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>  
  27.            <interceptor name="validation"class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>  
  28.            <interceptor name="workflow"class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>  
  29.            <interceptor name="store"class="org.apache.struts2.interceptor.MessageStoreInterceptor"/>  
  30.            <interceptor name="checkbox"class="org.apache.struts2.interceptor.CheckboxInterceptor"/>  
  31.            <interceptor name="profiling"class="org.apache.struts2.interceptor.ProfilingActivationInterceptor"/>  
  32.            <interceptor name="roles"class="org.apache.struts2.interceptor.RolesInterceptor"/>  
  33.            <interceptor name="jsonValidation"class="org.apache.struts2.interceptor.validation.JSONValidationInterceptor"/>  
  34.            <interceptornameinterceptorname="annotationWorkflow"class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor"/>  


The interceptor with sturts2 is relatively convenient to use. We only need to add <interceptor-ref name="logger"/> to the action tag of struts.xml and extend struts.xml to struts-default.

If you want to customize the interceptor, you first need to write an interceptor class:

  1. package ceshi;  
  2. import com.opensymphony.xwork2.ActionInvocation;  
  3. import com.opensymphony.xwork2.interceptor.AbstractInterceptor;  
  4.    
  5. publicclassAuthorizationInterceptor extends AbstractInterceptor {  
  6.    
  7.     @Override  
  8.     public Stringintercept(ActionInvocation ai)throws Exception {  
  9.          
  10.            System.out.println("abc");  
  11.             return ai.invoke();  
  12.              
  13.     }  
  14.    
  15. }  

And configure it in struts.xml

  1. <!DOCTYPEstruts PUBLIC  
  2. "-//Apache SoftwareFoundation//DTD Struts Configuration 2.0//EN"  
  3. "http://struts.apache.org/dtds/struts-2.0.dtd">  
  4.    
  5.    
  6. <struts>  
  7.     <package name="test"extends="struts-default">  
  8.      <interceptors>  
  9.       <interceptor name="abc"class ="ceshi.AuthorizationInterceptor"/>  
  10.     </interceptors>  
  11.         <action name="TestLogger"class="vaannila.TestLoggerAction">  
  12.            <interceptor-refnameinterceptor-refname="abc"/>  
  13.            <result name="success">/success.jsp</result>  
  14.            </action>  
  15.     </package>  
  16. </struts>  


8. Once the Action is executed, the Action Invocation is responsible for finding the corresponding return result according to the configuration in struts.xml. The return result is usually (but not always, or possibly another Action chain) a template of a JSP or FreeMarker that needs to be represented. The tags inherited from the Struts 2 framework can be used in the presentation process. ActionMapper needs to be involved in this process


All objects (Action, Results, Interceptors, etc.) are created through ObjectFactory in the above process.

 

Comparisons between Struts 2 and struts 1

Strts2 is much simpler and more powerful than struts1. We can see from several aspects:

Architecturally speaking, struts2 uses interceptors to make requests, which allows it to be separated from the business logic controller and servlet-api, thus avoiding intrusion; while struts1.x obviously intrudes servlet-api in action.

Thread security analysis: struts 2.x is thread-safe, and each object generates an instance to avoid thread security problems; while struts 1.x belongs to single thread in action.

Performance: struts 2.x test It can be detached from web containers, while struts 1.x relies on servlet-api, and testing relies on Web containers.

Request parameter encapsulation comparison: struts 2.x uses the Model Driven mode, so that we can encapsulate model objects directly without inheriting any base classes of struts 2 to avoid intrusion.

Advantages of tags: tag libraries can almost completely replace JSTL tag libraries, and struts 2.x supports powerful ognl expressions.

Of course, struts2 is much more convenient than struts1 in terms of file upload, data validation, etc. I will not go into details here.

Posted by dino345 on Fri, 19 Apr 2019 18:51:35 -0700