• 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);
            }
        }
  • 相关阅读:
    关于android表单多字段布局的一些思考
    thrift 试用
    关于带角度的箭头
    同步和异步
    storm各组件基本概念
    AWK使用实例
    Hotspot虚拟机目录结构
    大数据生态
    代码生成器 架构提取工具tqdemo
    [源码]C# to SQL 的翻译器.net 1.1版
  • 原文地址:https://www.cnblogs.com/gudu1/p/7726172.html
Copyright © 2020-2023  润新知