Struts2工作流程个人解析

前面稍微学习了下Strust2基本使用,对Struts2的工作流程以及底层源码完全不懂,今天打算把Struts2的工作流程好好的摸索一遍。

1.这是一张网上download的struts2工作流程图,

对上图稍做解释:

1.首先客户端/浏览器发送一个请求到服务器,即HttpServletRequest会经过一系列(Fliter)过滤器(ActionContextCleanUp该过滤器是可选过滤器主要作用就是对ActionContext进行CleanUp操作,不让后续的Fliter清除,延长action中属性的生命周期,以便在jsp中访问。

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                chain.doFilter(request, response);
            } else {
                prepare.setEncodingAndLocale(request, response);//对locale、encoding进行设置
                prepare.createActionContext(request, response);//创建AcionContext,即action上下文
                prepare.assignDispatcherToThread();
                request = prepare.wrapRequest(request);//对request进行包装
                ActionMapping mapping = prepare.findActionMapping(request, response, true);//得到action mapping
				//如果mapping为空,则不会调用action,会调用下一个过滤器链,直到获取到mapping才调用action
                if (mapping == null) {
                    boolean handled = execute.executeStaticResourceRequest(request, response);
                    if (!handled) {
                        chain.doFilter(request, response);
                    }
				//不为空时,则执行action
                } else {
                    execute.executeAction(request, response, mapping);
                }
            }
        } finally {
            prepare.cleanupRequest(request);
        }
    }

    public void destroy() {
        prepare.cleanupDispatcher();
    }

}

在调用完所有的doFilter方法后,核心过滤器StrutsPrepareAndExecuteFilter会清空ActionContext,避免内存泄漏。如果其他过滤器还想使用ValueStack中的sturts属性,如果不使用ActionContextCleanUp便无法得到,说白了就是说清理ActionContext的工作就交给ActionContextCleanUp,其他过滤器不用去管,这样action属性的生命周期就延长了。  ActionContextCleanUp工作原理:

在doFilter中设置一个counter计数器,当请求来的时候回被赋1,然后放到请求中。有了这个计数器,后续的Fliter就不会清空ActionContext了,而是由ActionContextCleanUp这个过滤器负责清除。

private static final String COUNTER = "__cleanup_recursion_counter";

try {
            UtilTimerStack.push(timerKey);

            try {
                Integer count = (Integer)request.getAttribute(COUNTER); //从request中取出counter值,当请求来的时候,request作用域中没有counter值,所以被赋null
                if (count == null) {
                    count = Integer.valueOf(1);//counter值为null,请求刚来,因为赋1
                }
                else {
                    count = Integer.valueOf(count.intValue()+1);//不为1,+1
                }
                request.setAttribute(COUNTER, count);//把标记的count值放到request作用域当中

                //LOG.debug("filtering counter="+count);

                chain.doFilter(request, response);//执行doFliter,把请求传给下一个Fliter
            } finally {
                int counterVal = ((Integer)request.getAttribute(COUNTER)).intValue();//取出counter值
                counterVal -= 1;//-1,为清除做准备
                request.setAttribute(COUNTER, Integer.valueOf(counterVal));//更新request作用域中的counter值
                cleanUp(request);//调用cleanup方法清除数据
            }
    }

	//COUNTER>0或非空则不进行清除
	 protected static void cleanUp(ServletRequest req) {
			// should we clean up yet?
			Integer count = (Integer) req.getAttribute(COUNTER);
			if (count != null && count > 0 ) {
				if (LOG.isDebugEnabled()) {
					LOG.debug("skipping cleanup counter="+count);
				}
				return;
			}

			// always dontClean up the thread request, even if an action hasn't been executed
			ActionContext.setContext(null);
			Dispatcher.setInstance(null);
		}
不过在struts2的2.1.3版本该方法已经被摈弃了,而是直接在doFilter最后调用cleanUp这个方法(原理一样):
public void cleanupRequest(HttpServletRequest request) {
        Integer counterVal = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
        if (counterVal != null) {
            counterVal -= 1;
            request.setAttribute(CLEANUP_RECURSION_COUNTER, counterVal);
            if (counterVal > 0 ) {
                if (log.isDebugEnabled()) {
                    log.debug("skipping cleanup counter="+counterVal);
                }
                return;
            }
        }
        // always clean up the thread request, even if an action hasn't been executed
        try {
            dispatcher.cleanUpRequest(request);
        } finally {
            ActionContext.setContext(null);
            Dispatcher.setInstance(null);
        }
    }

这里解释下ActionContext是什么,里面放的是什么:

ActionContext是Struts2的上下文,负责存储action运行产生的数据(主要存储request、session、application、parameters等相关信息),结构是key-value的map集合,可以像map一样进行操作,ActionContext生命周期都是一次Http请求。

Strus2会根据每个执行Http请求的线程来创建对应的ActionContext,即一个线程有一个唯一的ActionContex。可以使用ActionContext.getContext()获取当前线程的ActionContext(actioncontext是threadloacl线程绑定的),可能是这个原因不需要担心Action的线程安全。

static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();

	private Map<String, Object> context;

	public ActionContext(Map<String, Object> context) {
        this.context = context;
}

2.就是核心过滤器StrutsPrepareAndExecuteFilter被调用,它会询问ActionMapper是否要调用Action,调用哪个Action,如果ActionMapper决定需要调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy来处理

3.ActionProxy会通过configurationManager询问配置文件,找到相应的Action类。

 4. ActionProxy创建一个ActionInvocation的实例,ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。

<strong>这里把2-4的源码局部源码一起呈现上来:</strong>
//Action的配置信息存储在ActionMapping对象中
		public class ActionMapping {

			private String name;
			private String namespace;
			private String method;
			private String extension;
			private Map<String, Object> params;
			private Result result;
			...
		}

		ActionInvocation getInvocation();

		 try {
            UtilTimerStack.push(timerKey);
            String namespace = mapping.getNamespace(); //从mapping对象获取命名空间
            String name = mapping.getName();           //获取请求的action名
            String method = mapping.getMethod();       //获取请求方法

			//得到配置对象
            Configuration config = configurationManager.getConfiguration();

			//根据执行上下文参数,命名空间,名称等创建用户自定义Action的代理对象
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, method, extraContext, true, false);

            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

            // if the ActionMapping says to go straight to a result, do it!
			//如果配置文件中执行的这个action配置了result,就直接转到resul
            if (mapping.getResult() != null) {
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {
                proxy.execute();
            }

            // If there was a previous value stack then set it back onto the request
            if (!nullStack) {
                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
            }
        } 

5. 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是一个JSP页面。

Struts 2设计的精巧之处就是使用了Action代理Action代理可以根据系统的配置,加载一系列的拦截器,由拦截器将HttpServletRequest参数解析出来,传入Action。

同样,Action处理的结果也是通过拦截器传入HttpServletResponse,然后由HttpServletRequest传给用户。

拦截器是Struts 2框架的核心,通过拦截器,实现了AOP(面向切面编程)。但建议在编写Action的时候,尽量避免将业务逻辑放到其中,尽量减少Action与业务逻辑模块或者组件的耦合程度。

Struts2的源码很多,值得深究,可能上述的理解有误或不全面,欢迎各位指出,加油!(其中参考了这篇博文:http://www.cnblogs.com/liuling/p/2013-8-10-01.html)

时间: 2024-10-10 00:21:59

Struts2工作流程个人解析的相关文章

Struts2工作流程

Struts2是一个非常优秀的MVC框架,它主要通过StrutsPrepareAndExecuteFilter过滤器将Struts2集成到Web应用中的. 基本工作流程: 1.客户端提交一个HttpServletRequest请求(action或JSP页面): 2.请求经过一系列的过滤器(如ActionContextCleanUp,SiteMesh等): 3.Struts2的核心过滤器StrutsPrepareAndExecuteFilter被调用,并询问ActionMapper来决定这个请求是

Struts2 工作流程

在我的理解中Struts2的工作流程大概分为七步: 1.客户端发送HttpServletRequest请求: 2.这个请求经过一系列的过滤器(Filter),最后发送到FilterDispatcher中: 3.FilterDispatcher通过action mapper确定是否有对应的action,如果有,FilterDispatcher就发送到actionproxy中: 4.再由actionproxy查询Struts.xml中对应的action类, 5.actionproxy实例化一个新的对

Struts2的工作流程

Struts2的工作流程 1.客户端浏览器初始化时发出HTTP请求 2.根据web.xml配置,上述请求被FilterDispatcher接收 3.根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton 4.Action调用业务逻辑组件处理业务逻辑 5.Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面 6.返回HTTP响应到客户端浏览器

Struts2 核心流程

1.Struts2架构图  这是Struts2官方站点提供的Struts 2 的整体结构.  执行流程图 2.Struts2部分类介绍  这部分从Struts2参考文档中翻译就可以了. ActionMapper         ActionMapper其实是HttpServletRequest和Action调用请求的一个映射,它屏蔽了Action对于Request等 java Servlet类的依赖.Struts2中它的默认实现类是DefaultActionMapper,ActionMapper

SSH三大框架的各自工作流程

一.Struts2的工作流程:1.用户在客户端发起请求,客户端会初始化一个servlet容器请求:2.servlet容器把请求会传递给context容器,context容器找到目标web工程.3.进行解析web.xml中的struts标签中的配置: <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPr

01SpringMvc_初识工作流程

我们首先要知道用SpringMVC开发需要什么包? 我们需要导入springioc,springweb , springmvc相关的jar包. 有关Spring的jar包的用途如下: 所以我们用Springmvc开发是的jar包如下: 在讲解SpringMVC的工作流程之前先介绍下Struts2.的工作流程. 第一步:发送请求到服务器,产生了HttpServletRequest和请求,这个请求经过了other filters过滤器和Strute2的核心过滤器之后来到了ActionMapper(S

面试题:SpringMVC的工作流程

SpringMVC是当今最主流的Web MVC框架,没有之一,要做一名合格的JavaWeb工程师,学好它势在必行! 与Struts2原理不同,SpringMVC是通过最基础最传统的servlet来实现对框架源代码的封装以及对整个流程的控制的,而Struts2是通过过滤器来实现URL路径与具体Action的对应关系确认的.(Struts2具体机制参看另一篇博客链接) 下图是springMVC的原理示意图: SpringMVC工作流程概述: 1.客户端向web服务器(如tomcat)发送一个http

Android ListView工作原理完全解析(转自 郭霖老师博客)

原文地址:http://blog.csdn.net/guolin_blog/article/details/44996879 在Android所有常用的原生控件当中,用法最复杂的应该就是ListView了,它专门用于处理那种内容元素很多,手机屏幕无法展示出所有内容的情况.ListView可以使用列表的形式来展示内容,超出屏幕部分的内容只需要通过手指滑动就可以移动到屏幕内了. 另外ListView还有一个非常神奇的功能,我相信大家应该都体验过,即使在ListView中加载非常非常多的数据,比如达到

Struts2学习第一天——struts2基本流程与配置

struts2框架 什么是框架,框架有什么用? 框架 是 实现部分功能的代码 (半成品),使用框架简化企业级软件开发 ,提高开发效率. 学习框架 ,清楚的知道框架能做什么? 还有哪些工作需要自己编码实现 ? 什么是struts2框架,它有什么用? Struts 2是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架. 其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大.Struts 2以WebWork为核心 struts2=struts1+