Analysis of struts2 request processing process

Source: Internet
Author: User

Like starting struts2, it also has an entry, that is, the doFilter method of org. apache. struts2.dispatcher. ng. filter. StrutsPrepareAndExecuteFilter.

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        try {            prepare.setEncodingAndLocale(request, response);            prepare.createActionContext(request, response);            prepare.assignDispatcherToThread();if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {chain.doFilter(request, response);} else {request = prepare.wrapRequest(request);ActionMapping mapping = prepare.findActionMapping(request, response, true);if (mapping == null) {boolean handled = execute.executeStaticResourceRequest(request, response);if (!handled) {chain.doFilter(request, response);}} else {execute.executeAction(request, response, mapping);}}        } finally {            prepare.cleanupRequest(request);        }    }

This part includes setting encoding, creating actioncontext, and setting the Distance variable to the local copy instance of this thread.

private static ThreadLocal
 
   instance = new ThreadLocal
  
   ();
  
 

Next, obtain actionmapping. This actionmapping matches the settings in the configuration file according to the uri of our request to get the corresponding action.

public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {        ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);        if (mapping == null || forceLookup) {            try {                mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());                if (mapping != null) {                    request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);                }            } catch (Exception ex) {                dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);            }        }        return mapping;    }


Do you still remember the principle of dispatcher. getContainer (). getInstance (ActionMapper. class? As mentioned in the previous article, in the initialization of struts2, the container has been created successfully, and the container contains the map of factories, each item is composed of a Key consisting of name and type and the Value of the corresponding object factory. Let's see how getMapping is implemented.

public ActionMapping getMapping(HttpServletRequest request,                                    ConfigurationManager configManager) {        ActionMapping mapping = new ActionMapping();        String uri = getUri(request);        int indexOfSemicolon = uri.indexOf(";");        uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;        uri = dropExtension(uri, mapping);        if (uri == null) {            return null;        }        parseNameAndNamespace(uri, mapping, configManager);        handleSpecialParameters(request, mapping);        if (mapping.getName() == null) {            return null;        }        parseActionName(mapping);        return mapping;    }    protected ActionMapping parseActionName(ActionMapping mapping) {        if (mapping.getName() == null) {            return mapping;        }        if (allowDynamicMethodCalls) {            // handle "name!method" convention.            String name = mapping.getName();            int exclamation = name.lastIndexOf("!");            if (exclamation != -1) {                mapping.setName(name.substring(0, exclamation));                mapping.setMethod(name.substring(exclamation + 1));            }        }        return mapping;    }


protected void parseNameAndNamespace(String uri, ActionMapping mapping,                                         ConfigurationManager configManager) {        String namespace, name;        int lastSlash = uri.lastIndexOf("/");        if (lastSlash == -1) {            namespace = "";            name = uri;        } else if (lastSlash == 0) {            // ww-1046, assume it is the root namespace, it will fallback to            // default            // namespace anyway if not found in root namespace.            namespace = "/";            name = uri.substring(lastSlash + 1);        } else if (alwaysSelectFullNamespace) {            // Simply select the namespace as everything before the last slash            namespace = uri.substring(0, lastSlash);            name = uri.substring(lastSlash + 1);        } else {            // Try to find the namespace in those defined, defaulting to ""            Configuration config = configManager.getConfiguration();            String prefix = uri.substring(0, lastSlash);            namespace = "";            boolean rootAvailable = false;            // Find the longest matching namespace, defaulting to the default            for (Object cfg : config.getPackageConfigs().values()) {                String ns = ((PackageConfig) cfg).getNamespace();                if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {                    if (ns.length() > namespace.length()) {                        namespace = ns;                    }                }                if ("/".equals(ns)) {                    rootAvailable = true;                }            }            name = uri.substring(namespace.length() + 1);            // Still none found, use root namespace if found            if (rootAvailable && "".equals(namespace)) {                namespace = "/";            }        }        if (!allowSlashesInActionNames && name != null) {            int pos = name.lastIndexOf('/');            if (pos > -1 && pos < name.length() - 1) {                name = name.substring(pos + 1);            }        }        mapping.setNamespace(namespace);        mapping.setName(name);    }


This code should be simple. It is nothing more than parsing the request uri, obtaining its namespace, name, method, and so on, and setting it to actionmapping. After obtaining actionmapping, the system starts to process the request.

execute.executeAction(request, response, mapping);
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {        dispatcher.serviceAction(request, response, servletContext, mapping);    }
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,                              ActionMapping mapping) throws ServletException {        Map
 
   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);        boolean nullStack = stack == null;        if (nullStack) {            ActionContext ctx = ActionContext.getContext();            if (ctx != null) {                stack = ctx.getValueStack();            }        }        if (stack != null) {            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.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, method, extraContext, true, false);            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();            }           //................    }
 

The previous section deals with actioncontext and valuestack. In createContextMap, the native request and response are encapsulated into the map type, and then an overloaded method of createContextMap is called to encapsulate these native requests, session and encapsulated map requests and sessions are put into a large map. If you want to obtain these objects in action, you can also.

For example, Map Request = (Map ) ActionContext. getContext (). get ("request"); the string used for Map-type requests is "request". If you want to obtain a native request, it is also possible, but not "request", but StrutsStatics. HTTP_REQUEST:

Httprequest = (HttpServletRequest) ActionContext. getContext (). get (StrutsStatics. HTTP_REQUEST); In map, the key is StrutsStatics. HTTP_REQUEST.

But you may ask: why can the session be obtained like this? Map Session = ActionContext. getContext (). getSession (); but does the request need to pass the get method?

This is because the getRequest method is not provided in ActionContext, and you do not know why it is not provided. In fact, getSession () is implemented through the get method.

Next, obtain the ing namespace, action name, and action method name. Let's execute the action. Do you just need to know this. Then obtain the proxy of an action: ActionProxy proxy.

public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
 
   extraContext, boolean executeResult, boolean cleanupContext) {                ActionInvocation inv = new DefaultActionInvocation(extraContext, true);        container.inject(inv);        return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);    }
 

Create actioninvocation. The container IOC mechanism is used for injection.

public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {                StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);        container.inject(proxy);        proxy.prepare();        return proxy;    }

In this case, the action proxy object is created and will be executed later.

protected void prepare() {        String profileKey = "create DefaultActionProxy: ";        try {            UtilTimerStack.push(profileKey);            config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);            //.........            resolveMethod();            if (!config.isAllowedMethod(method)) {                throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);            }            invocation.init(this);        } //.........    }
public void init(ActionProxy proxy) {        this.proxy = proxy;        Map
 
   contextMap = createContextMap();        // Setting this so that other classes, like object factories, can use the ActionProxy and other        // contextual information to operate        ActionContext actionContext = ActionContext.getContext();        if (actionContext != null) {            actionContext.setActionInvocation(this);        }        createAction(contextMap);        if (pushAction) {            stack.push(action);            contextMap.put("action", action);        }        invocationContext = new ActionContext(contextMap);        invocationContext.setName(proxy.getActionName());        // get a new List so we don't get problems with the iterator if someone changes the list        List
  
    interceptorList = new ArrayList
   
    (proxy.getConfig().getInterceptors());        interceptors = interceptorList.iterator();    }
   
  
 

The preceding settings are also used to set the context. I will not analyze the settings.

Actioninvocation is initialized here. After actioninvocation, We need to execute a series of interceptor and real actions, so these things must be initialized.

Return to serviceAction. Because we have obtained actionmapping and actionproxy. Then you can execute interceptor and action.

Their execution is to execute the next interceptor if there is an interceptor. If there is no interceptor, they will execute the real action and adopt a responsibility chain mode. This part is very simple and I will not analyze it. After the action is executed, each interceptor is returned in turn, and then each valve in each container of the web server (for example, StandardContextValve of the StandardContext container ). This completes the entire process.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.