Struts2源码学习(四)——Struts2处理Http请求

  在经典组合三大框架Struts2,Hibernate,Spring框架中,Struts2作为MVC中的View框架,其主要的实现的功能就是处理Http请求。在这篇文章中,我们将从源码的角度,来了解Struts2是如何处理Http请求的。

  在之前的文章介绍中,我们在对Struts2框架的分析中,一直都离不开的两大模块,第一是负责连接外部元素与Java对象的关系,第二则是作为Struts2基础的XWork框架,负责将请求分配给XWork框架中的不同元素进行处理。

  事实上,Struts2处理Http请求时,也主要分成对Http预处理阶段以及XWork事件处理这两个阶段,

  

 

  首先通过图片给Struts2处理流程有个大概的印象,接下来,我们通过源码的层面,来深入了解这个处理Http的流程。

  学习过Struts2框架的都知道,Struts2的入口类是配置在web.xml文件中的StrutsPrepareAndExecuteFilter类,这个类继承自我们所熟知的Filter接口,之前我们分析过这个类中的init方法,而现在,我们将深入查看这个类的核心逻辑处理方法doFilter,

 

 1 public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
 2     //  之前文章中分析过的Http预处理以及逻辑执行类
 3     protected PrepareOperations prepare;
 4     protected ExecuteOperations execute;
 5 
 6     //  核心逻辑方法
 7     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
 8         HttpServletRequest request = (HttpServletRequest)req;
 9         HttpServletResponse response = (HttpServletResponse)res;
10 
11         try {
12             String uri = RequestUtils.getUri(request);
13          
14             //  是否被过滤的URL
15             if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) {
16                 LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
17                 chain.doFilter(request, response);
18             } else {
19                 LOG.trace("Checking if {} is a static resource", uri);
20 
21                 //  是否处理静态资源
22                 boolean handled = this.execute.executeStaticResourceRequest(request, response);
23                 if (!handled) {
24                     LOG.trace("Assuming uri {} as a normal action", uri);
25                     //  设置编码和Locale
26                     this.prepare.setEncodingAndLocale(request, response);
27                    //  创建ActionContext
28                     this.prepare.createActionContext(request, response);
29                     // 为当前线程分配Dispatcher
30                     this.prepare.assignDispatcherToThread();
31                     //  封装 Request对象
32                     request = this.prepare.wrapRequest(request);
33                     //  根据request 查找 ActionMapping
34                     ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
35                     if (mapping == null) {
36                         LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
37                         chain.doFilter(request, response);
38                     } else {
39                         LOG.trace("Found mapping {} for {}", mapping, uri);
40                        //  执行URL的处理
41                         this.execute.executeAction(request, response, mapping);
42                     }
43                 }
44             }
45         } finally {
46              // 清理Request
47             this.prepare.cleanupRequest(request);
48         }
49 
50     }
51      
52     //  省略其他代码。,。。。          
53 }

 

  结合注释,整体的代码还是可以看得懂,其中的大部分方法最终都是对Dispatcher类的方法的调用。接下来对其中的方法进行深入了解。

  (1)设置编码的setEncodingAndLocale方法,

1 public class PrepareOperations {
2     public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {
3         this.dispatcher.prepare(request, response);
4     }
5  
6     。。。。。。
7 }

  转为对Dispatcher方法的调用,

 1 public class Dispatcher {
 2 
 3     public void prepare(HttpServletRequest request, HttpServletResponse response) {
 4         String encoding = null;
 5         //  设置默认的  Encoding
 6         if (this.defaultEncoding != null) {
 7             encoding = this.defaultEncoding;
 8         }
 9 
10         if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
11             encoding = "UTF-8";
12         }
13         // 设置 Locale
14         Locale locale = this.getLocale(request);
15         if (encoding != null) {
16             this.applyEncoding(request, encoding);
17         }
18 
19         if (locale != null) {
20             response.setLocale(locale);
21         }
22 
23         if (this.paramsWorkaroundEnabled) {
24             request.getParameter("foo");
25         }
26 
27     }
28 
29 
30 }

  (2)创建ActionContext的方法createActionContext,

 1 public class PrepareOperations {
 2 
 3     public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
 4  
 5         //  维护一个方便cleanup方法的计数器
 6         Integer counter = Integer.valueOf(1);
 7         Integer oldCounter = (Integer)request.getAttribute("__cleanup_recursion_counter");
 8         if (oldCounter != null) {
 9             counter = oldCounter.intValue() + 1;
10         }
11         // 获取当前线程的 ActionContext
12         ActionContext oldContext = ActionContext.getContext();
13         ActionContext ctx;
14         if (oldContext != null) {
15             //  若不为空, 复制成新的 ActionContext
16             ctx = new ActionContext(new HashMap(oldContext.getContextMap()));
17         } else {
18             //  若为空,创建 ValueStack,使用ValueStack的Context创建一个ActionContext
19             ValueStack stack = ((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
20             stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
21             ctx = new ActionContext(stack.getContext());
22         }
23 
24         request.setAttribute("__cleanup_recursion_counter", counter);
25         //  ActionContext 设置到当前线程中
26         ActionContext.setContext(ctx);
27         return ctx;
28     }
29 30     。。。。。
31 }

  (3)为当前线程分配Dispatcher的assignDispatcherToThread方法,

1 public class PrepareOperations {
2 
3     public void assignDispatcherToThread() {
4         Dispatcher.setInstance(this.dispatcher);
5     }
6 
7 }

  又是调用Dispatcher中的方法,

1 public class Dispatcher {
2 
3     public static Dispatcher getInstance() {
4         return (Dispatcher)instance.get();
5     }
6 
7     。。。。。。。
8 }

   (4)封装HttpServletRequest的wrapRequest方法

 1 public class Dispatcher {
 2 
 3     public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
 4         // 已经封装的则直接返回
 5         if (request instanceof StrutsRequestWrapper) {
 6             return request;
 7         } else {
 8            // 根据request的content-type不同封装成不同的装饰类
 9             Object request;
10             if (this.isMultipartSupportEnabled(request) && this.isMultipartRequest(request)) {
11                 MultiPartRequest multiPartRequest = this.getMultiPartRequest();
12                 LocaleProviderFactory localeProviderFactory = (LocaleProviderFactory)this.getContainer().getInstance(LocaleProviderFactory.class);
13                 request = new MultiPartRequestWrapper(multiPartRequest, request, this.getSaveDir(), localeProviderFactory.createLocaleProvider(), this.disableRequestAttributeValueStackLookup);
14             } else {
15                 // 封装成 StrutsRequestWrapper 类
16                 request = new StrutsRequestWrapper(request, this.disableRequestAttributeValueStackLookup);
17             }
18 
19             return (HttpServletRequest)request;
20         }
21     }
22 
23      。。。。。。
24 
25 }

  (5)根据Http查找ActionMapping的findActionMapping方法

 1 public class PrepareOperations {
 2 
 3     public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response) {
 4         return this.findActionMapping(request, response, false);
 5     }
 6 
 7     public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
 8         ActionMapping mapping = (ActionMapping)request.getAttribute("struts.actionMapping");
 9         if (mapping == null || forceLookup) {
10             try {
11                 // 使用 ActionMapper的实现类查找AcitonMapping
12                 mapping = ((ActionMapper)this.dispatcher.getContainer().getInstance(ActionMapper.class)).getMapping(request, this.dispatcher.getConfigurationManager());
13                 if (mapping != null) {
14                     request.setAttribute("struts.actionMapping", mapping);
15                 }
16             } catch (Exception var6) {
17                 if (this.dispatcher.isHandleException() || this.dispatcher.isDevMode()) {
18                     this.dispatcher.sendError(request, response, 500, var6);
19                 }
20             }
21         }
22 
23         return mapping;
24     }
25 
26     。。。。。。
27 }

  其中要注意的是, ActionMapper负责处理URL映射关系的问题,有多个不同的实现类,

  可以看出,这里又是Struts2使用设计模式中的策略模式的一个地方。

   (6)执行逻辑的executeAction方法

 1 public class ExecuteOperations {
 2     private Dispatcher dispatcher;
 3 
 4     public ExecuteOperations(Dispatcher dispatcher) {
 5         this.dispatcher = dispatcher;
 6     }
 7     //  处理静态资源的方法
 8     public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
 9         String resourcePath = RequestUtils.getServletPath(request);
10         if ("".equals(resourcePath) && null != request.getPathInfo()) {
11             resourcePath = request.getPathInfo();
12         }
13 
14         StaticContentLoader staticResourceLoader = (StaticContentLoader)this.dispatcher.getContainer().getInstance(StaticContentLoader.class);
15         if (staticResourceLoader.canHandle(resourcePath)) {
16             staticResourceLoader.findStaticResource(resourcePath, request, response);
17             return true;
18         } else {
19             return false;
20         }
21     }
22 
23     // 调用 Dispatcher 的方法, 执行核心逻辑的地方
24     public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
25         this.dispatcher.serviceAction(request, response, mapping);
26     }
27 }
 1 public class Dispatcher {
 2 
 3     //  Dispatcher的核心方法
 4     public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
 5 
 6         //  总体的数据环境
 7         Map<String, Object> extraContext = this.createContextMap(request, response, mapping);
 8 
 9         //  创建ValueStack, 若有,则做一个简单的复制, 若没有,从ActionContext获取当前线程的ValueStack
10         ValueStack stack = (ValueStack)request.getAttribute("struts.valueStack");
11         boolean nullStack = stack == null;
12         if (nullStack) {
13             ActionContext ctx = ActionContext.getContext();
14             if (ctx != null) {
15                 stack = ctx.getValueStack();
16             }
17         }
18 
19         if (stack != null) {
20         //  将ValueStack设置到数据环境中
21             extraContext.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this.valueStackFactory.createValueStack(stack));
22         }
23 
24         String timerKey = "Handling request from Dispatcher";
25 
26         try {
27             UtilTimerStack.push(timerKey);
28 
29             //  还记得这三个元素吗, 从ActionMapping中获取Action的三个要素
30             String namespace = mapping.getNamespace();
31             String name = mapping.getName();
32             String method = mapping.getMethod();
33 
34            //  获取ActionProxy,进入了XWork框架中
35             ActionProxy proxy = ((ActionProxyFactory)this.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy(namespace, name, method, extraContext, true, false);
36             request.setAttribute("struts.valueStack", proxy.getInvocation().getStack());
37 
38             // 若ActionMapping中包含Result,则执行Result的方法,否则执行ActionProxy的调用
39             if (mapping.getResult() != null) {
40                 Result result = mapping.getResult();
41                 result.execute(proxy.getInvocation());
42             } else {
43                 proxy.execute();
44             }
45 
46             if (!nullStack) {
47                 request.setAttribute("struts.valueStack", stack);
48             }
49         } catch (ConfigurationException var17) {
50             this.logConfigurationException(request, var17);
51             this.sendError(request, response, 404, var17);
52         } catch (Exception var18) {
53             var18.printStackTrace();
54             if (!this.handleException && !this.devMode) {
55                 throw new ServletException(var18);
56             }
57 
58             this.sendError(request, response, 500, var18);
59         } finally {
60             UtilTimerStack.pop(timerKey);
61         }
62 
63     }
64 
65     。。。。。。。
66 
67 }

  核心调度器Dispatcher负责对XWork框架中的元素进行调用。在进入XWork框架之后,Struts2的工作已经完成,接下来都交给XWork框架进行处理了。在这段代码中,可以看出,XWork中的ActionProxy,正是XWrok框架的入口,起到一个转接的作用。

  接下来,我们将对XWork入口——ActionProxy进行分析。

  在Dispatcher的serviceAction方法中,在获取ActionProxy的方法中,我们可以看到其中的四个参数——namespace, actionName, methodName和extraContext,而这些,正是构成ActionProxy数据源的参数。在这数据源中,前三个参数是在ActionMapper根据Http获取ActionMapping对象时获取的,而extraContext这个上下文对象,是通过Dispatcher中的createContextMap方法创建的。进入ActionInvocation中的createContextMap方法,查看ActionProxy如何初始化XWork的这个上下文环境的,

 1 public class DefaultActionInvocation implements ActionInvocation {
 2     protected Map<String, Object> extraContext;
 3     protected Map<String, Object> createContextMap() {
 4         Map contextMap;
 5         // 已经包含ValueStack, 使用ValueStack的上下文
 6         if (this.extraContext != null && this.extraContext.containsKey("com.opensymphony.xwork2.util.ValueStack.ValueStack")) {
 7             this.stack = (ValueStack)this.extraContext.get("com.opensymphony.xwork2.util.ValueStack.ValueStack");
 8             if (this.stack == null) {
 9                 throw new IllegalStateException("There was a null Stack set into the extra params.");
10             }
11 
12             contextMap = this.stack.getContext();
13         } else {
14             //  没有ValueStack, 创建一个ValueStack
15             this.stack = this.valueStackFactory.createValueStack();
16             contextMap = this.stack.getContext();
17         }
18         //  将 extraContext中的内容放入上下文环境中
19         if (this.extraContext != null) {
20             contextMap.putAll(this.extraContext);
21         }
22 
23         //  将 ActionInvocation 和  Container 放入 上下文环境中
24         contextMap.put("com.opensymphony.xwork2.ActionContext.actionInvocation", this);
25         contextMap.put("com.opensymphony.xwork2.ActionContext.container", this.container);
26         return contextMap;
27     }
28 
29     。。。。。。
30 }

   通过之前的文章,我们已经知道,ActionProxy只是提供Struts2和XWork之间联系的一个桥梁,正真负责对XWork框架元素的调用是ActionInvocation。 ActionInvocation的初始化过程在DefaultAcitionInvocation的init方法中,

 

 1 public class DefaultActionInvocation implements ActionInvocation {
 2 
 3     public void init(ActionProxy proxy) {
 4         this.proxy = proxy;
 5         // 创建上下文环境
 6         Map<String, Object> contextMap = this.createContextMap();
 7         ActionContext actionContext = ActionContext.getContext();
 8         // 将ActionInvocation设置到ActionContext中,让ActionInvocation可以共享
 9         if (actionContext != null) {
10             actionContext.setActionInvocation(this);
11         }
12 
13         // 创建 Action 对象
14         this.createAction(contextMap);
15 
16        // 这里是数据流与控制流相互关联的重要的一步, 将Action放入到ValueStack中
17         if (this.pushAction) {
18             this.stack.push(this.action);
19             contextMap.put("action", this.action);
20         }
21         // 将ActionInvocation的上下文和ActionContext等同
22         this.invocationContext = new ActionContext(contextMap);
23         this.invocationContext.setName(proxy.getActionName());
24         //  获取拦截器堆栈
25         this.createInterceptors(proxy);
26         this.prepareLazyParamInjector(this.invocationContext.getValueStack());
27     }
28 
29     。。。。。。。。
30 }

 

  查看DefaultActionInvocation的invoke方法,

 1 public class DefaultActionInvocation implements ActionInvocation {
 2 
 3     public String invoke() throws Exception {
 4         String profileKey = "invoke: ";
 5 
 6         String var21;
 7         try {
 8             UtilTimerStack.push(profileKey);
 9 
10             // 若已经执行过,直接抛异常
11             if (this.executed) {
12                 throw new IllegalStateException("Action has already executed");
13             }
14 
15            // 对Interceptor集合的调用
16             if (this.interceptors.hasNext()) {
17                 InterceptorMapping interceptorMapping = (InterceptorMapping)this.interceptors.next();
18                 String interceptorMsg = "interceptorMapping: " + interceptorMapping.getName();
19                 UtilTimerStack.push(interceptorMsg);
20 
21                 try {
22                     Interceptor interceptor = interceptorMapping.getInterceptor();
23                     if (interceptor instanceof WithLazyParams) {
24                         interceptor = this.lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), this.invocationContext);
25                     }
26 
27                     //  这里 intercept方法实际上包含了一个递归调用
28                     this.resultCode = interceptor.intercept(this);
29                 } finally {
30                     UtilTimerStack.pop(interceptorMsg);
31                 }
32             } else {
33                 //  递归中不再有 interceptor时,直接调用action
34                 this.resultCode = this.invokeActionOnly();
35             }
36 
37             if (!this.executed) {
38                 if (this.preResultListeners != null) {
39                     LOG.trace("Executing PreResultListeners for result [{}]", this.result);
40                     Iterator i$ = this.preResultListeners.iterator();
41                     //  对扩展点类的调用
42                     while(i$.hasNext()) {
43                         Object preResultListener = i$.next();
44                         PreResultListener listener = (PreResultListener)preResultListener;
45                         String _profileKey = "preResultListener: ";
46 
47                         try {
48                             UtilTimerStack.push(_profileKey);
49                             listener.beforeResult(this, this.resultCode);
50                         } finally {
51                             UtilTimerStack.pop(_profileKey);
52                         }
53                     }
54                 }
55                 // 执行Result对象
56                 if (this.proxy.getExecuteResult()) {
57                     this.executeResult();
58                 }
59 
60                 this.executed = true;
61             }
62 
63             var21 = this.resultCode;
64         } finally {
65             UtilTimerStack.pop(profileKey);
66         }
67 
68         return var21;
69     }
70 
71     。。。。。。。。。
72 }                

  这里说的其实就是在之前文章说提到过的,在XWork框架中,ActionInvocation对Interceptor,Action和Result整个执行栈的调用。查看调用Action的invokeActionOnly方法,

 1 public class DefaultActionInvocation implements ActionInvocation {
 2 
 3     public String invokeActionOnly() throws Exception {
 4         return this.invokeAction(this.getAction(), this.proxy.getConfig());
 5     }
 6 
 7     protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
 8         String methodName = this.proxy.getMethod();
 9         LOG.debug("Executing action method = {}", methodName);
10         String timerKey = "invokeAction: " + this.proxy.getActionName();
11 
12         try {
13             Throwable t;
14             try {
15                 UtilTimerStack.push(timerKey);
16 
17                 Object methodResult;
18                 try {
19                     methodResult = this.ognlUtil.callMethod(methodName + "()", this.getStack().getContext(), action);
20                 } catch (MethodFailedException var16) {
21                     if (!(var16.getReason() instanceof NoSuchMethodException)) {
22                         throw var16;
23                     }
24 
25                     if (!this.unknownHandlerManager.hasUnknownHandlers()) {
26                         throw var16;
27                     }
28 
29                     try {
30                         methodResult = this.unknownHandlerManager.handleUnknownMethod(action, methodName);
31                     } catch (NoSuchMethodException var15) {
32                         throw var16;
33                     }
34 
35                     if (methodResult == null) {
36                         throw var16;
37                     }
38                 }
39 
40                 String var20 = this.saveResult(actionConfig, methodResult);
41                 return var20;
42             } catch (NoSuchPropertyException var17) {
43                 throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + this.getAction().getClass() + "");
44             } catch (MethodFailedException var18) {
45                 t = var18.getCause();
46                 if (this.actionEventListener != null) {
47                     String result = this.actionEventListener.handleException(t, this.getStack());
48                     if (result != null) {
49                         String var8 = result;
50                         return var8;
51                     }
52                 }
53             }
54 
55             if (t instanceof Exception) {
56                 throw (Exception)t;
57             } else {
58                 throw var18;
59             }
60         } finally {
61             UtilTimerStack.pop(timerKey);
62         }
63     }
64 
65     。。。。。。。。。。。
66 }

  只是对Action中定义的方法的调用以及逻辑判断。

  至此,Struts2处理Http请求的流程已经结束。从StrutsPrepareAndExecuteFilter的doFilter方法出发,到PrepareOperation和ExecuteOperation类对Dispatcher类的方法的封装的调用,到从ActionProxy进入XWork框架后交由XWork框架全权进行逻辑处理,执行最后我们自己定义的Action的方法。整个流程下来,怎么说,荡气回肠。。。(〃'▽'〃)


   Struts2源码的学习博客到此结束,写了四篇总结了对《Struts2技术内幕》这本书的一些理解吧,算是读后感。

  写这篇文章的时候广行发面试短信了,周围的人都拿到offer了,我就零零星星这几个面试,心态BOOM,加油骚年 ̄へ ̄

 

posted @ 2017-10-20 22:25  VimKeyLin  阅读(1478)  评论(0编辑  收藏  举报