Struts2执行流程

来源:互联网 发布:js图片轮播和点击切换 编辑:程序博客网 时间:2024/06/05 22:18



1. 请求示意图
 
2. 一个请求在Struts2框架中的处理步骤:
1. 客户端初始化一个指向Servlet容器的请求
2. 根据web.xml配置,请求先经过ActionContextCleanUp过滤器,主要清理当前线程中ActionContext和Dispatcher
3. 请求经过插件过滤器(该步可以略过)
4. 请求经过StrutsPrepareAndExecuteFilter核心过滤器,执行doFilter方法,在该方法中,询问ActionMapper来决定这个请求是否需要调用Action
5. 如果ActionMapper决定调用某个Action,则ActionMapper会返回一个ActionMapping实例(存储Action的配置文件),并创建ActionProxy(Action代理)对象,将请求交给代理对象继续处理
6. ActionProxy对象根据ActionMapping和Configuration Manager询问框架的配置文件,找到需要调用的Action类
7. ActionProxy对象创建时,会同时创建一个ActionInvocation的实例
8. ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器的调用
9. 一旦Action执行完毕,ActionInvocation实例负责struts.xml中的配置创建并返回到result结果视图,result通常是一个需要被表示的JSP或者FreeMarker的模板,也可能是另外的一个Action链
10. 如果要在返回result之前做些什么,可以实现PreResultListener接口,PreResultListener可以在Interceptor中实现,也可以在Action中实现
11. 根据Result对象信息,生成用户响应信息response

3. 源码分析阶段
3.1  服务器启动阶段
1. 服务器启动后,进入web.xml,跳进配置的核心过滤器StrutsPrepareAndExecuteFilter
2. 类内部首先是三个变量的声明
  1. protected PrepareOperations prepare;
  2. protected ExecuteOperations execute;
  3. protected List<Pattern> excludedPatterns = null;
3. 进入到init方法
  1. public void init(FilterConfig filterConfig) throws ServletException {
  2. InitOperations init = new InitOperations();
  3. Dispatcher dispatcher = null;
  4. try {
  5. FilterHostConfig config = new FilterHostConfig(filterConfig);
  6. init.initLogging(config);
  7. dispatcher = init.initDispatcher(config);
  8. init.initStaticContentLoader(config, dispatcher);
  9. prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
  10. execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
  11. this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
  12. postInit(dispatcher, filterConfig);
  13. } finally {
  14. if (dispatcher != null) {
  15. dispatcher.cleanUpAfterInit();
  16. }
  17. init.cleanup();
  18. }
  19. }
4.对于第5行
  1. FilterHostConfig config = new FilterHostConfig(filterConfig);
进入到FilterHostConfig内部查看,发现FilterHostConfig主要是用来获取Servlet上下文、web.xml中的init-param配置的param-name获取param-value的值
  1. public class FilterHostConfig implements HostConfig {
  2. private FilterConfig config;
  3. public FilterHostConfig(FilterConfig config) {
  4. this.config = config;
  5. }
  6. public String getInitParameter(String key) {
  7. return config.getInitParameter(key);
  8. }
  9. public Iterator<String> getInitParameterNames() {
  10. return MakeIterator.convert(config.getInitParameterNames());
  11. }
  12. public ServletContext getServletContext() {
  13. return config.getServletContext();
  14. }
  15. }

dispatcher中serviceAction方法
  1. /**
  2. * Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.
  3. 加载Action类用于映射,执行相应的Action方法,或者直接转到Result视图
  4. * <p/>
  5. * This method first creates the action context from the given parameters,
  6. 这个方法首先从给定的参数创建action上下文
  7. * and then loads an <tt>ActionProxy</tt> from the given action name and namespace.
  8. 然后,从给定的action名和名称空间中加载一个ActionProxy
  9. * After that, the Action method is executed and output channels through the response object.
  10. 之后,action方法被执行以及通过响应对象输出通道
  11. * Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,
  12. Action如果没有发现,将通过sendError方法返回用户提示
  13. * using the 404 return code.
  14. 使用404返回代码
  15. * All other errors are reported by throwing a ServletException.
  16. 其他的错误将被抛出ServletException异常
  17. *
  18. * @param request HttpServletRequest 对象
  19. * @param response HttpServletResponse对象
  20. * @param mapping ActionMapping对象
  21. * @throws ServletException when an unknown error occurs (not a 404, but typically something that
  22. * would end up as a 5xx by the servlet container)
  23. * @param context Our ServletContext object
  24. */
  25. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
  26. ActionMapping mapping) throws ServletException {
  27. Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
  28. // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
  29. //如果先前有一个值栈,那么创建一个新的副本,并传递给新的Action使用
  30. //从request中获取到ValueStack对象
  31. ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
  32. //判断ValueStack是否为空
  33. boolean nullStack = stack == null;
  34. if (nullStack) {
  35. //为空,获取ActionContext
  36. ActionContext ctx = ActionContext.getContext();
  37. if (ctx != null) {
  38. //如果ActionContext不为空,通过ActionContext获取ValueStack---通过查看ActionContext,发现ActionContext包含ValueStack,
  39. stack = ctx.getValueStack();
  40. }
  41. }
  42. if (stack != null) {
  43. //把ValueStack放入ActionContext中
  44. extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
  45. }
  46. String timerKey = "Handling request from Dispatcher";
  47. try {
  48. UtilTimerStack.push(timerKey);
  49. String namespace = mapping.getNamespace();
  50. String name = mapping.getName();
  51. String method = mapping.getMethod();
  52. Configuration config = configurationManager.getConfiguration();
  53. //加载ActionProxy
  54. ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
  55. namespace, name, method, extraContext, true, false);
  56. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
  57. // if the ActionMapping says to go straight to a result, do it!
  58. //如果ActionMapping决定直接返回到结果视图,完成
  59. if (mapping.getResult() != null) {
  60. Result result = mapping.getResult();
  61. result.execute(proxy.getInvocation());
  62. } else {
  63. //否则往下执行
  64. proxy.execute();
  65. }
  66. // If there was a previous value stack then set it back onto the request
  67. if (!nullStack) {
  68. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
  69. }
  70. } catch (ConfigurationException e) {
  71. // WW-2874 Only log error if in devMode
  72. if (devMode) {
  73. String reqStr = request.getRequestURI();
  74. if (request.getQueryString() != null) {
  75. reqStr = reqStr + "?" + request.getQueryString();
  76. }
  77. LOG.error("Could not find action or result\n" + reqStr, e);
  78. } else {
  79. if (LOG.isWarnEnabled()) {
  80. LOG.warn("Could not find action or result", e);
  81. }
  82. }
  83. sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
  84. } catch (Exception e) {
  85. if (handleException || devMode) {
  86. sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
  87. } else {
  88. throw new ServletException(e);
  89. }
  90. } finally {
  91. UtilTimerStack.pop(timerKey);
  92. }
  93. }


1. 请求示意图
 
2. 一个请求在Struts2框架中的处理步骤:
1. 客户端初始化一个指向Servlet容器的请求
2. 根据web.xml配置,请求先经过ActionContextCleanUp过滤器,主要清理当前线程中ActionContext和Dispatcher
3. 请求经过插件过滤器(该步可以略过)
4. 请求经过StrutsPrepareAndExecuteFilter核心过滤器,执行doFilter方法,在该方法中,询问ActionMapper来决定这个请求是否需要调用Action
5. 如果ActionMapper决定调用某个Action,则ActionMapper会返回一个ActionMapping实例(存储Action的配置文件),并创建ActionProxy(Action代理)对象,将请求交给代理对象继续处理
6. ActionProxy对象根据ActionMapping和Configuration Manager询问框架的配置文件,找到需要调用的Action类
7. ActionProxy对象创建时,会同时创建一个ActionInvocation的实例
8. ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器的调用
9. 一旦Action执行完毕,ActionInvocation实例负责struts.xml中的配置创建并返回到result结果视图,result通常是一个需要被表示的JSP或者FreeMarker的模板,也可能是另外的一个Action链
10. 如果要在返回result之前做些什么,可以实现PreResultListener接口,PreResultListener可以在Interceptor中实现,也可以在Action中实现
11. 根据Result对象信息,生成用户响应信息response

3. 源码分析阶段
3.1  服务器启动阶段
1. 服务器启动后,进入web.xml,跳进配置的核心过滤器StrutsPrepareAndExecuteFilter
2. 类内部首先是三个变量的声明
  1. protected PrepareOperations prepare;
  2. protected ExecuteOperations execute;
  3. protected List<Pattern> excludedPatterns = null;
3. 进入到init方法
  1. public void init(FilterConfig filterConfig) throws ServletException {
  2. InitOperations init = new InitOperations();
  3. Dispatcher dispatcher = null;
  4. try {
  5. FilterHostConfig config = new FilterHostConfig(filterConfig);
  6. init.initLogging(config);
  7. dispatcher = init.initDispatcher(config);
  8. init.initStaticContentLoader(config, dispatcher);
  9. prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
  10. execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
  11. this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
  12. postInit(dispatcher, filterConfig);
  13. } finally {
  14. if (dispatcher != null) {
  15. dispatcher.cleanUpAfterInit();
  16. }
  17. init.cleanup();
  18. }
  19. }
4.对于第5行
  1. FilterHostConfig config = new FilterHostConfig(filterConfig);
进入到FilterHostConfig内部查看,发现FilterHostConfig主要是用来获取Servlet上下文、web.xml中的init-param配置的param-name获取param-value的值
  1. public class FilterHostConfig implements HostConfig {
  2. private FilterConfig config;
  3. public FilterHostConfig(FilterConfig config) {
  4. this.config = config;
  5. }
  6. public String getInitParameter(String key) {
  7. return config.getInitParameter(key);
  8. }
  9. public Iterator<String> getInitParameterNames() {
  10. return MakeIterator.convert(config.getInitParameterNames());
  11. }
  12. public ServletContext getServletContext() {
  13. return config.getServletContext();
  14. }
  15. }

dispatcher中serviceAction方法
  1. /**
  2. * Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.
  3. 加载Action类用于映射,执行相应的Action方法,或者直接转到Result视图
  4. * <p/>
  5. * This method first creates the action context from the given parameters,
  6. 这个方法首先从给定的参数创建action上下文
  7. * and then loads an <tt>ActionProxy</tt> from the given action name and namespace.
  8. 然后,从给定的action名和名称空间中加载一个ActionProxy
  9. * After that, the Action method is executed and output channels through the response object.
  10. 之后,action方法被执行以及通过响应对象输出通道
  11. * Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,
  12. Action如果没有发现,将通过sendError方法返回用户提示
  13. * using the 404 return code.
  14. 使用404返回代码
  15. * All other errors are reported by throwing a ServletException.
  16. 其他的错误将被抛出ServletException异常
  17. *
  18. * @param request HttpServletRequest 对象
  19. * @param response HttpServletResponse对象
  20. * @param mapping ActionMapping对象
  21. * @throws ServletException when an unknown error occurs (not a 404, but typically something that
  22. * would end up as a 5xx by the servlet container)
  23. * @param context Our ServletContext object
  24. */
  25. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
  26. ActionMapping mapping) throws ServletException {
  27. Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
  28. // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
  29. //如果先前有一个值栈,那么创建一个新的副本,并传递给新的Action使用
  30. //从request中获取到ValueStack对象
  31. ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
  32. //判断ValueStack是否为空
  33. boolean nullStack = stack == null;
  34. if (nullStack) {
  35. //为空,获取ActionContext
  36. ActionContext ctx = ActionContext.getContext();
  37. if (ctx != null) {
  38. //如果ActionContext不为空,通过ActionContext获取ValueStack---通过查看ActionContext,发现ActionContext包含ValueStack,
  39. stack = ctx.getValueStack();
  40. }
  41. }
  42. if (stack != null) {
  43. //把ValueStack放入ActionContext中
  44. extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
  45. }
  46. String timerKey = "Handling request from Dispatcher";
  47. try {
  48. UtilTimerStack.push(timerKey);
  49. String namespace = mapping.getNamespace();
  50. String name = mapping.getName();
  51. String method = mapping.getMethod();
  52. Configuration config = configurationManager.getConfiguration();
  53. //加载ActionProxy
  54. ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
  55. namespace, name, method, extraContext, true, false);
  56. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
  57. // if the ActionMapping says to go straight to a result, do it!
  58. //如果ActionMapping决定直接返回到结果视图,完成
  59. if (mapping.getResult() != null) {
  60. Result result = mapping.getResult();
  61. result.execute(proxy.getInvocation());
  62. } else {
  63. //否则往下执行
  64. proxy.execute();
  65. }
  66. // If there was a previous value stack then set it back onto the request
  67. if (!nullStack) {
  68. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
  69. }
  70. } catch (ConfigurationException e) {
  71. // WW-2874 Only log error if in devMode
  72. if (devMode) {
  73. String reqStr = request.getRequestURI();
  74. if (request.getQueryString() != null) {
  75. reqStr = reqStr + "?" + request.getQueryString();
  76. }
  77. LOG.error("Could not find action or result\n" + reqStr, e);
  78. } else {
  79. if (LOG.isWarnEnabled()) {
  80. LOG.warn("Could not find action or result", e);
  81. }
  82. }
  83. sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
  84. } catch (Exception e) {
  85. if (handleException || devMode) {
  86. sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
  87. } else {
  88. throw new ServletException(e);
  89. }
  90. } finally {
  91. UtilTimerStack.pop(timerKey);
  92. }
  93. }

0 0