标签: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、拦截器工作原理、method、result
原文地址:http://yoyanda.blog.51cto.com/9675421/1714677