Struts2笔记(2)——Struts的请求流程

Struts2.0简介

Struts2是阿帕奇旗下的一款流行的Web框架,基于MVC模式,成功结合了Webwok和Struts1.X的优势。虽然名字上叫做Struts2.0,但除此之外它和Struts1.X的关系并不大。
Struts2.0的核心功能都是使用拦截器实现“值栈”这个概念,OGNL表达式和Struts2.0标签来解决应用程序数据,以及大量的注解,使得这个框架更易于使用。这些优势足以说服我们使用Struts2.0作为Web开发的框架。

Struts2.0的请求流程

  1. 客户端发送一个请求到Servlet容器,也就是Tomcat之类的服务器;
  2. 请求经过一系列过滤器(其中有一个ActionContextCleanUp的过滤器,该过滤器可选,它的功能是在调用完doFilter()方法之后,决定cleanUp哪些ActionContext,以及,哪些不清空,以保证后续的filter不被清空,延长Action中属性的寿命);
  3. 接着调用核心过滤器(相当于前端控制器的角色,让我想起Spring中的DispatcherServlet),也就是StrutsPrepareAndExcuteFilter图中是FilterDispatcher,FilterDispatcher在早期的版本中使用的较多,在2.1.3之后就不再推荐使用,它会询问ActionMapper是否调用某个Action
  4. 如果ActionMapper决定调用某个Action返回对应Action的mapping信息给StrutsPrepareAndExcuteFilter,StrutsPrepareAndExcuteFilter再将mapping交给ActionProxy,由ActionProxy来通过ConfigurationManager询问配置文件,找到指定的Action,也就是上一篇中的struts.xml
  5. ActionProxy创建一个ActionInvocation实例,ActionInvocation实例使用命名模式来调用(这里的命名模式十分疑惑,经查证源码后发现,应当是从xml文件中读取Action的名称,然后用java反射的方式创建实例),在Action调用前后,涉及Interceptor(拦截器)相关调用。
  6. Action执行结束后,ActionInvocation负责根据struts.xml 中的配置找到返回结果。返回结果通常是一个需要被表示的jsp或者FreeMarker模板(但并不总是,也可能是另一个Action链)。
    这里写图片描述
    参考流程
    //TO BE CONTINUE
    我们有必要来研究一下源码:
    顺序上按照请求处理的顺序,首先,我们查看StrutsPrepareAndExcuteFilter的源码,其doFilter()方法如下:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        //获得请求
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;

        try {
            String uri = RequestUtils.getUri(request);
            //在这里判断请求是否交给Struts2处理
            if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) {
                LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
                chain.doFilter(request, response);
                //是交由Struts2处理的请求
            } else {
                LOG.trace("Checking if {} is a static resource", uri);
                boolean handled = this.execute.executeStaticResourceRequest(request, response);
                if (!handled) {
                    LOG.trace("Assuming uri {} as a normal action", uri);
                    this.prepare.setEncodingAndLocale(request, response);
                    //创建ActionContext
                    /*ValueStack stack = //((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
            stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
            ctx = new ActionContext(stack.getContext());
            */
            //以上是PrepareOperation的createActionContext()方法的核心代码,这里创建值栈,并且将请求参数等内容放入了值栈的Context部分,再用该值栈创建开一个新的ActionContext。
                    this.prepare.createActionContext(request, response);
                    this.prepare.assignDispatcherToThread();
                    //包装原生request对象,在此之前,它是apche包装的request对象,而之后则转换为Struts2的包装对象。
                    request = this.prepare.wrapRequest(request);
                    //将请求交给ActionMapping进行分析
                    ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
                    //判断mapping是否为空
                    if (mapping == null) {
                        LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
                        chain.doFilter(request, response);
                    } else {
                        LOG.trace("Found mapping {} for {}", mapping, uri);
                        this.execute.executeAction(request, response, mapping);
                    }
                }
            }
        } finally {
            this.prepare.cleanupRequest(request);
        }
  1. 获得请求;
  2. 在这里判断请求是否交给Struts2处理;
  3. 如果不交由Struts2处理,则继续交给拦截器中链处理,否则创建一个ActionContext。ActionContext由PrepareOperation的createActionContext()方法创建,方法内创建值栈,并且将请求参数等内容放入了值栈的Context部分,再用该值栈创建开一个新的ActionContext;
  4. 将原生request对象进行包装,将getAttribute()重新包装,包装之后的getAttribute()方法将先从原生域中查找对象,如果找不到,再从ValueStack的栈中查找,最后会在ValueStack中的Context中查找,这个过程在StrutsRequestWrapper的源码中可以看到;
  5. 使用ActionMapping从请求中抽取信息,调用PrepareOperation对象prepare的findActionMapping来查找actionMapping,该方法内部使用了ActionMapper来查找Action的配置信息,判断mapping是否为空,是则将其交由后续filterChain处理,否则执行调用excuteOperation对象执行excuteAction方法;
  6. 执行excuteAction方法,该方法是中间方法,它的内部执行了Dispatcher对象的serviceAction方法,根据mapping中包含的信息创建ActionProxy对象,调用proxy对象的excute方法,该方法内部使用了ActionInvocation的invoke方法,excuteAction方法代码如下
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
        Map<String, Object> extraContext = this.createContextMap(request, response, mapping);
        ValueStack stack = (ValueStack)request.getAttribute("struts.valueStack");
        boolean nullStack = stack == null;
        if (nullStack) {
            ActionContext ctx = ActionContext.getContext();
            if (ctx != null) {
                stack = ctx.getValueStack();
            }
        }

        if (stack != null) {
            extraContext.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this.valueStackFactory.createValueStack(stack));
        }

        String timerKey = "Handling request from Dispatcher";

        try {
            UtilTimerStack.push(timerKey);
            //从mapping中抽取所需属性信息
            String namespace = mapping.getNamespace();
            String name = mapping.getName();
            String method = mapping.getMethod();
            //创建ActionProxy对象
            ActionProxy proxy = ((ActionProxyFactory)this.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy(namespace, name, method, extraContext, true, false);
            request.setAttribute("struts.valueStack", proxy.getInvocation().getStack());
            if (mapping.getResult() != null) {
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {
            //该方法内部调用了invocation.invoke()方法,这个invocation是DefaultActionInvocation类型,其内部invoke()方法会使用集合类的hasNext()将当前invocation传入所需的拦截器链的intercept方法,当interceptor的所有intercept执行完毕后,调用invocation的invokeActionOnly方法,执行action。
                proxy.execute();
            }

            if (!nullStack) {
                request.setAttribute("struts.valueStack", stack);
            }
        } catch (ConfigurationException var17) {
            this.logConfigurationException(request, var17);
            this.sendError(request, response, 404, var17);
        } catch (Exception var18) {
            if (!this.handleException && !this.devMode) {
                throw new ServletException(var18);
            }

            this.sendError(request, response, 500, var18);
        } finally {
            UtilTimerStack.pop(timerKey);
        }

    }

7.执行Action。获得执行结果resultCode,也就是返回值”success”,接着判断结果是否执行,否则调用excuteResult(),然后就会调用各类result。

本文参考

struts2的核心和工作原理
Struts2工作流程个人解析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值