码迷,mamicode.com
首页 > 其他好文 > 详细

struct2源码解读(11)之执行action请求中篇

时间:2015-11-19 16:52:37      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:struct2、拦截器工作原理、method、result

struct2源码解读之执行action请求(2)

    上篇博文介绍了执行action请求前的一些准备工作,包括封装http参数到一个map中,获得一个值栈对象和配置信息configuration,并创建一个执行action请求的actionProxy对象,并对这个对象进行了初始化,包括指定默认执行方法和对actionName对应的类进行依赖注入以及调出拦截器。

     然后就到了我们执行action请求的工作。

 if (mapping.getResult() != null) {
         
        Result result = mapping.getResult();
        result.execute(proxy.getInvocation());
 } else {
        //执行action请求
        proxy.execute();
 }

    这里会先对封装了url信息的actionMapping对象进行判断,如果url中有result信息,则直接执行result,如果没有,则执行actionName中的方法。下面详细谈谈执行actionName中的方法。

一、执行method  

  public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        ActionContext.setContext(invocation.getInvocationContext());
        String retCode = null;  
        try { 
              //调用invocation.invoke执行action请求,返回一个字符串  	
            retCode = invocation.invoke();
        } finally {
            if (cleanupContext) {
                ActionContext.setContext(nestedContext);
            }
        }
        return retCode;
    }

    执行actionProxy的execute方法,其实执行的是actionInvocation的invoke()方法

 public String invoke() throws Exception {

        try {
           //迭代
            if (interceptors.hasNext()) {
             //当前拦截器
        final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
                try {
                 //1.执行拦截器
       resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                    }
                finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } else {
                //2.执行action请求
                resultCode = invokeActionOnly();
            }
            if (!executed) {
                if (preResultListeners != null) {
                   //如果配置了listener,执行
                }
                if (proxy.getExecuteResult()) {
                     //3.执行返回结果
                    executeResult();
                }
                executed = true;
            }
            return resultCode;
        }
   
    }

    从这里我们就可以看出,执行action请求其实主要做了三步工作

    ①执行拦截器

    ②执行action请求中的方法(如:execute)

    ③执行方法的返回结果(如retrun "success" 跳转到success指定的也面)


1.1.执行拦截器

    上篇博文我们已经调出了我们所需要的拦截器到了一个list集合(iterceptors)中。如果给你们设计,执行拦截器,你们第一反应想到的就是循环遍历拦截器里面的invoke()方法,就比如

    for(int i=0;i<interceptor.sizes();i++ ){

       final InterceptorMapping interceptor = (InterceptorMapping) interceptors.get(i);
       resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this) 
    }
    resultCode = invokeActionOnly();

     如果是这样设计的话,那就是要执行完所有的拦截器后,才可以执行action请求中的方法;如果我们想在执行action请求后,再用拦截器处理一些信息,这样的设计就达不到我们所要的目的。比如一个处理异常信息的拦截器,想从第一个拦截器执行开始try然后等action执行完后再catch,也就是action方法必须在这个拦截器中就执行,显然,上面的设计是不能实现的。struct2给出的解决方法就是迭代,在设计每个拦截器的时候都迭代invocation.invoke()方法,如默认拦截器栈中的第一个拦截器

 <interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                  <param name="excludeParams">dojo\..*,^struts\..*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
            </interceptor-stack>

     第一个拦截器是exception,我们找到这个拦截器

<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>

我们找到ExceptionMappingInterceptor的intecept()方法

 public String intercept(ActionInvocation invocation) throws Exception {
        String result;

        try {
             //迭代invocation.invoke()
            result = invocation.invoke();
        } catch (Exception e) {
           //异常信息处理
        }
        return result;
    }

     果然,struct2设计了一个拦截器对执行action请求的所有操作都用一个try-catch包起来了,当又迭代回到invocation.invoke()的时候

 //如果还有下一个拦截器
 if (interceptors.hasNext()) {
          //取得下一个拦截器
         final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();          
                try {
                 //调用当前拦截器的intercept方法
       resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                     }
                finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } else {
                resultCode = invokeActionOnly();
            }

     就会执行下一个拦截器,比如拦截器栈中的alias

<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>

      我们尚且不讨论,这个拦截器里面干了什么,但我敢肯定,这个拦截器里面必定有invocation.invoke()调用下一个拦截器

   @Override public String intercept(ActionInvocation invocation) throws Exception {

       //代码略
       
        return invocation.invoke();
    }

寻寻觅觅,我们在AliasInterceptor.intercept()中果真找到了这个,这样又会在第二个拦截器中掉用拦截器链中的下一个拦截器,一直去到最后一个拦截器workflow,在这个workflow也必定会有invocation.invoke()迭代invocation的invoke()方法,在最后一次迭代的时候,发现没拦截器了,就会执行action请求

 if (interceptors.hasNext()) {
       //没拦截器了,该逻辑不成立,调过
 } else {
      //执行action请求
     resultCode = invokeActionOnly();
}

需要特别主要的是,result请求,也是最后一次迭代中执行的

 代码清单:最后一次迭代 
    public String invoke() throws Exception {
   
        try {
            if (interceptors.hasNext()) {

                }
            } else {
                //执行action请求
                resultCode = invokeActionOnly();
            }

            if (!executed) {
                if (preResultListeners != null) {

                }
                //执行result
                if (proxy.getExecuteResult()) {
                    executeResult();
                }
                //给一个已经执行过的标志
                executed = true;
            }
            return resultCode;
        }
 
    }

    最后一次迭代的时候,executed为false的,执行result之后,把exceuted置为true,则返回到上一层迭代的时候,就不会执行result请求了,所以,第一次执行invocation.invoke()之后只是调用拦截器,之后的所有业务都在拦截器中进行了。如果精简了第一次执行invocation.invoke()的代码,则为

    public String invoke() throws Exception {
   
        try {
            if (interceptors.hasNext()) {
             //调用第一个拦截器
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
            
            } 
            return resultCode;
        }
 
    }

    后面的操作,都是通过在拦截器里面调用。需要特别注意的是,执行action请求的操作是在执行

invocation.invoke()中进行的,因此我们可以在invocation.invoke()前后加入自己的逻辑

如默认的第一个拦截器exception

 public String intercept(ActionInvocation invocation) throws Exception {
        String result;

        try {
           
            /*
             *1.在执行action请求前加入自己的逻辑
             */
             //迭代invocation.invoke(),执行action请求
            result = invocation.invoke();
            /*
             *2.在执行action请求后加入自己的逻辑
             */
        } catch (Exception e) {
           //异常信息处理
        }
        return result;
    }

    默认的拦截器栈的第一个拦截器是struct2设计的,我们不能修改它的代码,当我们可以自己设计一个拦截器,然后把它压入拦截器栈的栈顶,这样我们的拦截器就是第一个拦截器了。

    至于默认的拦截器栈的拦截器干了什么工作,我们下篇博文继续介绍,现在先来它是如何执行action求情的。


1.2.执行action请求

    我们上面讨论到,执行action请求,是在最后一个拦截器中调用invocation.invoke()方法中,发现没拦截器了,就执行invokeActionOnly执行的。

 protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
        //获取配置中的method
        String methodName = proxy.getMethod(); 
        try {
      
            boolean methodCalled = false;
            Object methodResult = null;
            Method method = null;
            try {
                //通过方法名找到actionName类中的方法
                method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
            } 
            if (!methodCalled) {//true
                //执行方法
                methodResult = method.invoke(action, new Object[0]);
            }
            //根据返回值(return),返回不同的类型结果(null或者是string)
            if (methodResult instanceof Result) {
                this.explicitResult = (Result) methodResult;
                container.inject(explicitResult);
                 //如果是对象实例,返回null
                return null;
            } else {
                return (String) methodResult;
            }
        }
        //异常信息,代码略
    }

   从这里看到,其实执行action请求,就是根据配置中的method,通过Method类,找到action类中的相应的方法,然后通过method类的invoke()方法,执行这个方法。


1.3.执行result

   在最后一次迭代invocation.invoke()的时候,也会执行相应的result

   //第一次执行result前,executed为false
   if (!executed) {
                if (preResultListeners != null) {

                }
                // 执行结果
                if (proxy.getExecuteResult()) {
                    executeResult();
                }
               //执行之后,把executed 置为true,确保这里只执行一次
                executed = true;
   }

这个executeResult是在实例化actionproxy的时候赋的值,初始为true

  代码清单:actionproxy构造函数
  protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
        
        this.invocation = inv;
	this.cleanupContext = cleanupContext;
	this.actionName = actionName;
	this.namespace = namespace;
	//executeResult
	this.executeResult = executeResult;
        this.method = methodName;
    }

我们再来看看,实例化actionproxy传进去的参数

ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);

那个true就是executeResult的值,由此可知,在最后一次迭代invocation.invoke()的时候,必定会执行executeResult()方法

    private void executeResult() throws Exception {
        /*
         *1.获取相应result类型的处理器
         */
        result = createResult();
        try {
            if (result != null) {
                /**
                  *2.调用相应的相应处理器的execute方法,执行相应的操作
                  */
                result.execute(this);
            } 
          //异常信息
    }

(1)获取相应result类型的处理器    

   我们知道,我们在配置structs.xml-result的时候有不同的类型,如dispatcher,redirect,redirectAction等等,配置不同的类型,就会有不同的结果。这个实现,就是在这里进行的。不同 的result类型用不同的对象来处理,如struct2-default.xml中配置的

        //默认类型
       <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
      <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
       <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
        <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
        <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>

通过createResult方法,取出相应的处理器

  public Result createResult() throws Exception {

        if (explicitResult != null) {
            Result ret = explicitResult;
            explicitResult = null;
            return ret;
        }
        /**
          *1.从actionConfig中找到resultConfig
          */
        ActionConfig config = proxy.getConfig();
        Map<String, ResultConfig> results = config.getResults();
        ResultConfig resultConfig = null;
        try {
            resultConfig = results.get(resultCode);
        }    
        if (resultConfig == null) {
          //如果找不到result,就用通配符
            resultConfig = results.get("*");
        }
        //
        if (resultConfig != null) {
            try {
            /**
              *根据resultConfig找到相应的对象
              */
       return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
            }
        }
        return null;
    }

    这个resultConfig是在初始化解析配置文件的时候,封装result的配置信息,包括封装了type信息的ResulTypeConfig

  public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
        //得到resultType的class,默认为dispacher
        String resultClassName = resultConfig.getClassName();
        Result result = null;

        if (resultClassName != null) {
            //实例化上面的class,内部用newInstance实现
            result = (Result) buildBean(resultClassName, extraContext);
            //获取result的参数params,封装到reflectionProvider对象中
            Map<String, String> params = resultConfig.getParams();
            if (params != null) {
                for (Map.Entry<String, String> paramEntry : params.entrySet()) {
                    try {
                        reflectionProvider.setProperty(paramEntry.getKey(), paramEntry.getValue(), result, extraContext, true);
                    }
                }
            }
        }

        return result;
    }

(2)执行相应处理器的excute方法

     上面我们取得的相应resultType类型的处理器,因此就可以执行相应的result操作.我们举它默认的类型dispacher 为例子。我们找到这个处理器的类

<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>

     在这个类的exceute方法中,先处理params,然后再执行

 public void execute(ActionInvocation invocation) throws Exception {
        //根式化ognl格式的params
        lastFinalLocation = conditionalParse(location, invocation);
        //执行
        doExecute(lastFinalLocation, invocation);
    }

    这里需要对result的parms进行处理,比如在文件下载的时候,配置的result

 <result name="download" type="stream">
	<param name="contentType">${mimeType}</param>
	<param name="contentDisposition">attachment;filename="${filename}"</param>
	<param name="inputName">inputStream</param>
	<param name="bufferSize">4096</param>
 </result>

   这里用到了ognl表达式,因此在处理result结果的时候,需要对这些进行解析。解析完后就执行相应的操作

 public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
    
        PageContext pageContext = ServletActionContext.getPageContext();

        if (pageContext != null) {
            pageContext.include(finalLocation);
        } else {
            HttpServletRequest request = ServletActionContext.getRequest();
            HttpServletResponse response = ServletActionContext.getResponse();
            RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);

            //add parameters passed on the location to #parameters
            // see WW-2120
            if (invocation != null && finalLocation != null && finalLocation.length() > 0
                    && finalLocation.indexOf("?") > 0) {
            String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
            Map parameters = (Map) invocation.getInvocationContext().getContextMap().get("parameters");
                Map queryParams = UrlHelper.parseQueryString(queryString, true);
                if (queryParams != null && !queryParams.isEmpty())
                    parameters.putAll(queryParams);
            }
            // 如果请求地址不存在,抛出404错误
            if (dispatcher == null) {
                response.sendError(404, "result ‘" + finalLocation + "‘ not found");
                return;
            }
            //跳转页面
            if (!response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {
                request.setAttribute("struts.view_uri", finalLocation);
                request.setAttribute("struts.request_uri", request.getRequestURI());
                 //<jsp:forward>
                dispatcher.forward(request, response);
            } else {
                //<jsp:include>
                dispatcher.include(request, response);
            }
        }
    }


二、总结

   本篇博文主要介绍了struct2是如何处理action请求的:通过invocation.invoke()调用拦截器链,在最后一个拦截器中执行action请求,先根据Method类找到actionName相关类的方法,然后调用method.invoke()执行,然后再用相关result类型的处理器,处理相应的result请求。下篇博文,将介绍每个拦截器的作用及应用。


本文出自 “总有贱人想害朕。” 博客,请务必保留此出处http://yoyanda.blog.51cto.com/9675421/1714677

struct2源码解读(11)之执行action请求中篇

标签:struct2、拦截器工作原理、method、result

原文地址:http://yoyanda.blog.51cto.com/9675421/1714677

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!