Struts2运行流程-源码剖析

Posted Mr丶苏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2运行流程-源码剖析相关的知识,希望对你有一定的参考价值。

 

  首先说一下查看这些框架源码的感受,每一次深入探究 Spring、Struts 等框架源码都有种深陷进去不能自拔的感觉,但是只要思路清晰,带着心中各种疑问去一点一点深入,还是会带给我很多欣喜,柳暗花明又一村的感觉,总的来说,这个工作并不是那么枯燥,感觉还是蛮不错的。

 本文为原创,如需转载,请标明出处:http://www.cnblogs.com/gudu1/p/7726172.html 

 

 下面代码是 StrutsPrepareAndExcuteFilter.java 中的代码

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

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                chain.doFilter(request, response);
            } else {
                prepare.setEncodingAndLocale(request, response);   
                prepare.createActionContext(request, response);   
                prepare.assignDispatcherToThread();   
                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);  
        }
    }
  相信用过 Struts2 的朋友都知道在项目搭建的过程中要在 web.xml 中配置 这个 Filter 类,它跟SpringMVC中的 DispatcherServlet 类的共同点是拦截请求然后进行一系列处理,那么Struts2的处理就是上面这个方法,看起来很简单对吗?事实上就是很简单,那么继续看:

     首先请求进入Tomcat 容器,由Tomcat创建 ServletRequest 和 ServletResponse 以及 FilterChain 对象并传入 doFilter(req,res,chain)。首先把 req 和 res 进行向下转型,接着判断web容器中是否还有过滤器,如果有就执行下一个过滤器,没有就进行请求资源:

  ·  ①:设置字符集编码和环境

     ②:创建 ActionContext 对象,使用的是 PrepareOperations.java 中的方法。

     ③:创建一个 Dispatcher 对象保存在当前线程中,如果该线程中存在直接返回,如果没有就创建一个并保存。

     ④:包装 HttPServletRequest 对象,如果是该请求是上传文件包装成Struts2中的 MultiPartRequestWrapper 对象,否者是 StrutsRequestWrapper 对象,然后把包装对象保存在 ActionContext 的 Map 集合中。

     ⑤:通过 URI 截取判断是请求静态资源还是请求Action,如果是请求静态资源直接返回 NULL,否则就在 request 中寻找 Action ,在Struts2中 request 对象是保存在值栈中的,如果存在就直接返回,没有就创建一个保存在request中并返回。

     ⑥ - ⑦:如果是请求静态资源,就执行 executeStaticResourceRequest() 方法,否则就执行 executeAction() 方法。

     ⑧:请求已经结束了,因为Struts2是多例,最后要销毁资源,比如 ActionContext,Dispatcher。

 

   上面 从第 ④ 步到最后的源码都会贴出来,因为代码众多,所以这里只贴出部分帮助理解:

  代码先就放在这里,日后有时间再写出解释。

  ④:

 

org.apache.struts2.dispatcher.ng.PrepareOperations

public
HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException { HttpServletRequest request = oldRequest; try { // Wrap request first, just in case it is multipart/form-data // parameters might not be accessible through before encoding (ww-1278) request = dispatcher.wrapRequest(request); ServletActionContext.setRequest(request); } catch (IOException e) { throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e); } return request; }

org.apache.struts2.dispatcher.Dispatcher.wrapRequest(HttpServletRequest)
public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException { // don\'t wrap more than once if (request instanceof StrutsRequestWrapper) { return request; } String content_type = request.getContentType(); if (content_type != null && content_type.contains("multipart/form-data")) { MultiPartRequest mpr = getMultiPartRequest(); LocaleProvider provider = getContainer().getInstance(LocaleProvider.class); request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup); } else { request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup); } return request; }

 

 

 ⑤:

org.apache.struts2.dispatcher.ng.PrepareOperations.findActionMapping(HttpServletRequest, HttpServletResponse, boolean)

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) { if (dispatcher.isHandleException() || dispatcher.isDevMode()) { dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); } } } return mapping; }

org.apache.struts2.dispatcher.mapper.DefaultActionMapper.getMapping(HttpServletRequest, ConfigurationManager)
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) { ActionMapping mapping = new ActionMapping(); String uri = RequestUtils.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); return parseActionName(mapping); }

 

 

  ⑥:

  

 
org.apache.struts2.dispatcher.ng.ExecuteOperations.executeStaticResourceRequest(HttpServletRequest, HttpServletResponse)

public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // there is no action in this request, should we look for a static resource? String resourcePath = RequestUtils.getServletPath(request); if ("".equals(resourcePath) && null != request.getPathInfo()) { resourcePath = request.getPathInfo(); } StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class); if (staticResourceLoader.canHandle(resourcePath)) { staticResourceLoader.findStaticResource(resourcePath, request, response); // The framework did its job here return true; } else { // this is a normal request, let it pass through return false; } }
org.apache.struts2.dispatcher.DefaultStaticContentLoader.findStaticResource(String, HttpServletRequest, HttpServletResponse)
public void findStaticResource(String path, HttpServletRequest request, HttpServletResponse response) throws IOException { String name = cleanupPath(path); for (String pathPrefix : pathPrefixes) { URL resourceUrl = findResource(buildPath(name, pathPrefix)); if (resourceUrl != null) { InputStream is = null; try { //check that the resource path is under the pathPrefix path String pathEnding = buildPath(name, pathPrefix); if (resourceUrl.getFile().endsWith(pathEnding)) is = resourceUrl.openStream(); } catch (IOException ex) { // just ignore it continue; } //not inside the try block, as this could throw IOExceptions also if (is != null) { process(is, path, request, response); return; } } } response.sendError(HttpServletResponse.SC_NOT_FOUND); }

 

 

  ⑦:

  org.apache.struts2.dispatcher.Dispatcher.serviceAction(HttpServletRequest, HttpServletResponse, ActionMapping)

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
            throws ServletException {

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

        // 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();

            ActionProxy proxy = 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();
            }

            // If there was a previous value stack then set it back onto the request
            if (!nullStack) {
                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
            }
        } catch (ConfigurationException e) {
            logConfigurationException(request, e);
            sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
        } catch (Exception e) {
            if (handleException || devMode) {
                sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            } else {
                throw new ServletException(e);
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

 

com.opensymphony.xwork2.DefaultActionProxy.execute()

public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        ActionContext.setContext(invocation.getInvocationContext());

        String retCode = null;

        String profileKey = "execute: ";
        try {
            UtilTimerStack.push(profileKey);

            retCode = invocation.invoke();
        } finally {
            if (cleanupContext) {
                ActionContext.setContext(nestedContext);
            }
            UtilTimerStack.pop(profileKey);
        }

        return retCode;
    }

 

com.opensymphony.xwork2.DefaultActionInvocation.invoke()

 public String invoke() throws Exception {
        String profileKey = "invoke: ";
        try {
            UtilTimerStack.push(profileKey);

            if (executed) {
                throw new IllegalStateException("Action has already executed");
            }

            if (interceptors.hasNext()) {
                final InterceptorMapping interceptor = interceptors.next();
                String interceptorMsg = "interceptor: " + interceptor.getName();
                UtilTimerStack.push(interceptorMsg);
                try {
                                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                            }
                finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } else {
                resultCode = invokeActionOnly();
            }

            // this is needed because the result will be executed, then control will return to the Interceptor, which will
            // return above and flow through again
            if (!executed) {
                if (preResultListeners != null) {
                    LOG.trace("Executing PreResultListeners for result [#0]", result);

                    for (Object preResultListener : preResultListeners) {
                        PreResultListener listener = (PreResultListener) preResultListener;

                        String _profileKey = "preResultListener: ";
                        try {
                            UtilTimerStack.push(_profileKey);
                            listener.beforeResult(this, resultCode);
                        }
                        finally {
                            UtilTimerStack.pop(_profileKey);
                        }
                    }
                }

                // now execute the result, if we\'re supposed to
                if (proxy.getExecuteResult()) {
                    executeResult();
                }

                executed = true;
            }

            return resultCode;
        }
        finally {
            UtilTimerStack.pop(profileKey);
        }
    }

 

 

  ⑧:

  org.apache.struts2.dispatcher.ng.PrepareOperations.cleanupRequest(HttpServletRequest)

 public void cleanupRequest(HttpServletRequest request) {
        Integer counterVal = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
        if (counterVal != null) {
            counterVal -= 1;
            request.setAttribute(CLEANUP_RECURSION_COUNTER, counterVal);
            if (counterVal > 0 ) {
                if (log.isDebugEnabled()) {
                    log.debug("skipping cleanup counter="+counterVal);
                }
                return;
            }
        }
        // always clean up the thread request, even if an action hasn\'t been executed
        try {
            dispatcher.cleanUpRequest(request);
        } finally {
            ActionContext.setContext(null);
            Dispatcher.setInstance(null);
        }
    }

 

以上是关于Struts2运行流程-源码剖析的主要内容,如果未能解决你的问题,请参考以下文章

[原创]java WEB学习笔记70:Struts2 学习之路-- struts2拦截器源码分析,运行流程

深度挖掘 RocketMQ底层源码「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(Pull模式-下)

2018.11.20 Struts2中对结果处理方式分析&struts2内置的方式底层源码剖析

Struts2剖析Struts2中的反射技术 ValueStack(值栈)

精华推荐 | 深入浅出 RocketMQ原理及实战「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(上篇)

深度挖掘RocketMQ底层源码「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行调度的流程(Pull模式)