struts2_源碼學習_doFilter()

續 init()

過濾器主要工作的方法doFilter():

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;

        try {
            //獲得請求的url
            String uri = RequestUtils.getUri(request);
            //該請求被struts排除,將請求傳遞到過濾器鏈
            if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) {
                LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
                chain.doFilter(request, response);
            } else {
                //接受該請求,檢查是否是靜態資源
                LOG.trace("Checking if {} is a static resource", uri);
                boolean handled = this.execute.executeStaticResourceRequest(request, response);
                if (!handled) {
                    //普通請求處理
                    LOG.trace("Assuming uri {} as a normal action", uri);
                    //準備工作
                    //設置編碼和地區信息
                    this.prepare.setEncodingAndLocale(request, response);
                    //設置上下文
                    this.prepare.createActionContext(request, response);
                    //保證線程安全的操作
                    this.prepare.assignDispatcherToThread();
                    //包裝請求
                    request = this.prepare.wrapRequest(request);
                    //獲得處理該請求的action
                    ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
                    if (mapping == null) {
                        //無法處理,傳遞到下一個過濾器
                        LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
                        chain.doFilter(request, response);
                    } else {
                        //處理請求
                        LOG.trace("Found mapping {} for {}", mapping, uri);
                        this.execute.executeAction(request, response, mapping);
                    }
                }
            }
        } finally {
            this.prepare.cleanupRequest(request);
        }

    }

 目錄

找到處理請求的Action信息

 生成執行Action的代理

通過代理執行Action處理請求

獲得Result 


找到處理請求的Action信息

    ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
package org.apache.struts2.dispatcher.PrepareOperation;
    
    public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
        ActionMapping mapping = (ActionMapping)request.getAttribute("struts.actionMapping");
        if (mapping == null || forceLookup) {
            try {
                //通過ActionMapper找到對應處理的Action信息
                mapping = ((ActionMapper)this.dispatcher.getContainer().getInstance(ActionMapper.class)).getMapping(request, this.dispatcher.getConfigurationManager());
                if (mapping != null) {
                    //將mapping放入request
                    request.setAttribute("struts.actionMapping", mapping);
                }
            } catch (Exception var6) {
                if (this.dispatcher.isHandleException() || this.dispatcher.isDevMode()) {
                    //服務器內部錯誤,狀態碼500
                    this.dispatcher.sendError(request, response, 500, var6);
                }
            }
        }

        return mapping;
    }
public class ActionMapping {
    private String name;
    private String namespace;
    private String method;
    private String extension;
    private Map<String, Object> params;
    private Result result;
    
    ...

}

通過dispatcher獲得container再獲得ActionMapper的操作在之前的文章中已經提及的很多了,這就是container的主要作用之一:獲取對象。 然後主要就是getMapper(request,this.dispatcher.getConfigurationManager()):

public class DefaultActionMapper implements ActionMapper {   
    public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
        ActionMapping mapping = new ActionMapping();
        String uri = RequestUtils.getUri(request);
        //最大長度
        int indexOfSemicolon = uri.indexOf(59);
        uri = indexOfSemicolon > -1 ? uri.substring(0, indexOfSemicolon) : uri;
        //對uri進行處理 如/index.jsp處理之後剩下null /Login處理剩下/Login 主要是去掉.action後綴
        uri = this.dropExtension(uri, mapping);
        if (uri == null) {
            return null;
        } else {
            //分析獲得name和namespace屬性 /Login -> name = "Login" namespace = "/"
            this.parseNameAndNamespace(uri, mapping, configManager);
            //處理特殊參數
            this.handleSpecialParameters(request, mapping);
            //分析Action Name 主要是動態方法(屬性allowDynamicMethodCalls)如果不允許則不做任何操作
            return this.parseActionName(mapping);
        }
    }

    ...
}

 對於其中方法的具體實現,不再深入。

生成執行Action的代理

接下來爲:

doFilter():
    this.execute.executeAction(request, response, mapping);

ExecuteOperation
    public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
        this.dispatcher.serviceAction(request, response, mapping);
    }
Dispatcher

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
        //獲得外部環境
        Map<String, Object> extraContext = this.createContextMap(request, response, mapping);
        //獲得值棧
        ValueStack stack = (ValueStack)request.getAttribute("struts.valueStack");
        boolean nullStack = stack == null;
        if (nullStack) {
            ActionContext ctx = ActionContext.getContext();
            if (ctx != null) {
                stack = ctx.getValueStack();
            }
        }
        //將值棧放入上下文
        if (stack != null) {
            extraContext.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this.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 = ((ActionProxyFactory)this.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy(namespace, name, method, extraContext, true, false);
            request.setAttribute("struts.valueStack", proxy.getInvocation().getStack());
            if (mapping.getResult() != null) {
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {
                //處理請求
                proxy.execute();
            }
            //
            if (!nullStack) {
                request.setAttribute("struts.valueStack", stack);
            }
        } catch (ConfigurationException var17) {
            this.logConfigurationException(request, var17);
            this.sendError(request, response, 404, var17);
        } catch (Exception var18) {
            var18.printStackTrace();
            if (!this.handleException && !this.devMode) {
                throw new ServletException(var18);
            }

            this.sendError(request, response, 500, var18);
        } finally {
            UtilTimerStack.pop(timerKey);
        }

    }

關鍵的一步就是生成代理對象proxy,在生成過程首先生成一個ActionInvocation對象,然後生成proxy,接着調用proxy.prepare()。

DefaultActionProxyFactory
    
    public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
        ActionInvocation inv = this.createActionInvocation(extraContext, true);
        this.container.inject(inv);
        return this.createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
    }

    protected ActionInvocation createActionInvocation(Map<String, Object> extraContext, boolean pushAction) {
        return new DefaultActionInvocation(extraContext, pushAction);
    }

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

關於ActionInvocation我們看看它擁有的一些字段信息:

public class DefaultActionInvocation implements ActionInvocation {
    private static final Logger LOG = LogManager.getLogger(DefaultActionInvocation.class);
    protected Object action;//action對象,處理請求
    protected ActionProxy proxy;
    protected List<PreResultListener> preResultListeners;
    protected Map<String, Object> extraContext;
    protected ActionContext invocationContext;
    protected Iterator<InterceptorMapping> interceptors;//攔截器
    protected ValueStack stack;//值棧
    protected Result result;
    protected Result explicitResult;
    protected String resultCode;
    protected boolean executed = false;
    protected boolean pushAction = true;
    protected ObjectFactory objectFactory;
    protected ActionEventListener actionEventListener;
    protected ValueStackFactory valueStackFactory;
    protected Container container;
    protected UnknownHandlerManager unknownHandlerManager;
    protected OgnlUtil ognlUtil;
    protected LazyParamInjector lazyParamInjector;

...

}

 從Action、Interceptors可以看出,ActionInvocation對象描述了Action運行的整個過程。prepare()最主要的應該是獲取運行時的配置信息,也就是<pakage>中的<method><interceptor><result>等信息。

DefualtActionProxy

protected void prepare() {
        String profileKey = "create DefaultActionProxy: ";

        try {
            UtilTimerStack.push(profileKey);
            ////這個主要從運行時的配置,也就是我們前面提到的DefaultConfiguration中的RuntimeConfiguration中獲得,ActionConfig中包含了interceptor、result、param等信息
            //獲得該請求對應的運行時配置信息,其實也就是struts.xml配置的<action>信息
            this.config = this.configuration.getRuntimeConfiguration().getActionConfig(this.namespace, this.actionName);
            if (this.config == null && this.unknownHandlerManager.hasUnknownHandlers()) {
                this.config = this.unknownHandlerManager.handleUnknownAction(this.namespace, this.actionName);
            }

            if (this.config == null) {
                throw new ConfigurationException(this.getErrorMessage());
            }
            //如果method屬性爲空 則設置爲默認的"execute"
            this.resolveMethod();
            if (!this.config.isAllowedMethod(this.method)) {
                throw new ConfigurationException(this.prepareNotAllowedErrorMessage());
            }
            //初始化invocation,包括interceptor的賦值
            this.invocation.init(this);
        } finally {
            UtilTimerStack.pop(profileKey);
        }

    }

通過代理執行Action處理請求

最終調用了ActionInvocation的invoke()方法:

StrutsActionProxy   

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

        String var2;
        try {
            var2 = this.invocation.invoke();
        } finally {
            if (this.cleanupContext) {
                ActionContext.setContext(previous);
            }

        }

        return var2;
    }

前面說了擁有一個Action實例和這個Action所依賴的攔截器實例。ActionInvocation會按照指定的順序去執行這些攔截器、Action以及相應的Result。

package com.opensymphony.xwork2.DefaultActionInvocation

public String invoke() throws Exception {
        String profileKey = "invoke: ";

        String var21;
        try {
            UtilTimerStack.push(profileKey);
            if (this.executed) {
                throw new IllegalStateException("Action has already executed");
            }
            //這裏是調用攔截器,注意interceptors是迭代器
            if (this.interceptors.hasNext()) {
                InterceptorMapping interceptorMapping = (InterceptorMapping)this.interceptors.next();
                String interceptorMsg = "interceptorMapping: " + interceptorMapping.getName();
                UtilTimerStack.push(interceptorMsg);

                try {
                    Interceptor interceptor = interceptorMapping.getInterceptor();
                    if (interceptor instanceof WithLazyParams) {
                        interceptor = this.lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), this.invocationContext);
                    }
                    //攔截器處理
                    this.resultCode = interceptor.intercept(this);
                } finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } else {
//!!!!                
                //執行action對應的方法並且返回狀態碼即對應<result>中SUCCESS等
                this.resultCode = this.invokeActionOnly();
            }
            //獲得調用action之前的監聽器
            if (!this.executed) {
                if (this.preResultListeners != null) {
                    LOG.trace("Executing PreResultListeners for result [{}]", this.result);
                    Iterator i$ = this.preResultListeners.iterator();

                    while(i$.hasNext()) {
                        Object preResultListener = i$.next();
                        PreResultListener listener = (PreResultListener)preResultListener;
                        String _profileKey = "preResultListener: ";

                        try {
                            UtilTimerStack.push(_profileKey);
                            listener.beforeResult(this, this.resultCode);
                        } finally {
                            UtilTimerStack.pop(_profileKey);
                        }
                    }
                }
                //獲得結果
                if (this.proxy.getExecuteResult()) {
                    this.executeResult();
                }

                this.executed = true;
            }

            var21 = this.resultCode;
        } finally {
            UtilTimerStack.pop(profileKey);
        }

        return var21;
    }

會依次執行相關的interceptor,然後invokeActionOnly()方法執行了action的對應處理請求的方法,並且放回一個結果碼。 

package com.opensymphony.xwork2.DefaultActionInvocation

    public String invokeActionOnly() throws Exception {
        return this.invokeAction(this.getAction(), this.proxy.getConfig());
    }


    protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
        String methodName = this.proxy.getMethod();
        LOG.debug("Executing action method = {}", methodName);
        String timerKey = "invokeAction: " + this.proxy.getActionName();

        try {
            Throwable t;
            try {
                UtilTimerStack.push(timerKey);

                Object methodResult;
                try {
                    methodResult = this.ognlUtil.callMethod(methodName + "()", this.getStack().getContext(), action);
                } catch (MethodFailedException var16) {
                    ...
                }

                String var20 = this.saveResult(actionConfig, methodResult);
                return var20;
            } catch (NoSuchPropertyException var17) {
                ...
            } catch (MethodFailedException var18) {
                ...
            }

            if (t instanceof Exception) {
                throw (Exception)t;
            } else {
                throw var18;
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

獲得Result 

private void executeResult() throws Exception {
        //包括location(資源地址)、編碼、端口等信息
        this.result = this.createResult();
        String timerKey = "executeResult: " + this.getResultCode();

        try {
            UtilTimerStack.push(timerKey);
            if (this.result != null) {
                //執行result
                this.result.execute(this);
            } else {
                if (this.resultCode != null && !"none".equals(this.resultCode)) {
                    throw new ConfigurationException("No result defined for action " + this.getAction().getClass().getName() + " and result " + this.getResultCode(), this.proxy.getConfig());
                }

                if (LOG.isDebugEnabled()) {
                    LOG.debug("No result returned for action {} at {}", this.getAction().getClass().getName(), this.proxy.getConfig().getLocation());
                }
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }

    }

 Result的實現類StrutsResultSupport、ServletDispatcherResult、ServletRedirectResult、ServletActionRedirectResult,看到這些名稱應該很熟悉,沒錯對應的應該是<result>中type屬性的值,也就是dispatcher、redirect、actionRedirect。在這裏我們的例子是登錄操作,最終調用的是ServletDispatcherResult.doExecute():

package org.apache.struts2.result;

public class ServletDispatcherResult extends StrutsResultSupport :
  
    public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
        LOG.debug("Forwarding to location: {}", finalLocation);
        PageContext pageContext = ServletActionContext.getPageContext();
        if (pageContext != null) {
            pageContext.include(finalLocation);
        } else {
            HttpServletRequest request = ServletActionContext.getRequest();
            HttpServletResponse response = ServletActionContext.getResponse();
            RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);
            if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf(63) > 0) {
                String queryString = finalLocation.substring(finalLocation.indexOf(63) + 1);
                HttpParameters parameters = this.getParameters(invocation);
                Map<String, Object> queryParams = this.urlHelper.parseQueryString(queryString, true);
                if (queryParams != null && !queryParams.isEmpty()) {
                    parameters = HttpParameters.create(queryParams).withParent(parameters).build();
                    invocation.getInvocationContext().setParameters(parameters);
                    invocation.getInvocationContext().getContextMap().put("parameters", parameters);
                }
            }

            if (dispatcher == null) {
                LOG.warn("Location {} not found!", finalLocation);
                response.sendError(404, "result '" + finalLocation + "' not found");
                return;
            }

            Boolean insideActionTag = (Boolean)ObjectUtils.defaultIfNull(request.getAttribute("struts.actiontag.invocation"), Boolean.FALSE);
            if (!insideActionTag && !response.isCommitted() && request.getAttribute("javax.servlet.include.servlet_path") == null) {
                request.setAttribute("struts.view_uri", finalLocation);
                request.setAttribute("struts.request_uri", request.getRequestURI());
                //請求派發
                dispatcher.forward(request, response);
            } else {
                dispatcher.include(request, response);
            }
        }

    }

可以看出主要的工作就是資源的定位和請求的派發。這就是整個請求處理過程的主要步驟和涉及到的代碼。具體框架對應的流程推薦參考這篇文章:https://blog.csdn.net/wjw0130/article/details/46371847

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章