`
zddava
  • 浏览: 243674 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Struts2的Action调用(三)

阅读更多
3. dispatcher.serviceAction(request, response, servletContext, mapping);

这个部分实现了具体的Action调用,由于代码很长,我们来一步一步解说。

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {
        Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
        ......

    }



(1) 这个方法首先创建了一个名称为extraContext的Map对象,我们来看看它到底是做什么用途的。

    public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
            ActionMapping mapping, ServletContext context) {

        Map requestMap = new RequestMap(request);// 封装了请求对象

        Map params = null;// 封装了http参数
        if (mapping != null) {
            params = mapping.getParams();// 从ActionMapping中获取Action的参数Map
        }
        Map requestParams = new HashMap(request.getParameterMap());
        if (params != null) {
            params.putAll(requestParams);// 并将请求中的参数也放入Map中
        } else {
            params = requestParams;
        }

        Map session = new SessionMap(request);// 封装了session

        Map application = new ApplicationMap(context);// 封装了ServletContext

	/* 将各个Map放入extraContext中 */
        Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
        extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
        return extraContext;
    }


这个就是#createContextMap()的源代码,它保存了request,session,application,mapping的信息,这些信息以后可以统一在此对象中查找。


(2) 接下来,

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {

        Map<String, Object> extraContext = createContextMap(request, response, mapping, context);

        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
        if (stack != null) {
            extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
	}
	......

    }


如果request中已经有了一个ValueStack对象,将其保存下来,留待以后恢复,并把它进行一些封装后也存入extraContext中。


(3) 接下来是一些准备工作,如,获取了namespace,name,method等。

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {

	......

        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
        if (stack != null) {
            extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
        }

        String timerKey = "Handling request from Dispatcher";
        try {
            UtilTimerStack.push(timerKey);
            String namespace = mapping.getNamespace();
            String name = mapping.getName();
            String method = mapping.getMethod();

            Configuration config = configurationManager.getConfiguration();

	    ......

    }



(4) 这一步就开始到重点了:构建一个ActionProxy对象,它负责对真实的Action进行调用,并可以在调用Action前后调用拦截器(Interceptor),源代码如下:

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {

	......

        try {

	    ......

            Configuration config = configurationManager.getConfiguration();
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, extraContext, true, false);
            proxy.setMethod(method);// 设定要调用的方法名

	    ......

    }


下面来看一下ActionProxyFactory的默认实现StrutsActionProxyFactory的#createActionProxy()是怎样构建ActionProxy对象的:

    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext,
            boolean executeResult, boolean cleanupContext) throws Exception {
        ActionProxy proxy = new StrutsActionProxy(namespace, actionName, extraContext, executeResult, cleanupContext);
        container.inject(proxy);
        proxy.prepare();
        return proxy;
    }


由上述的源代码可见,方法返回了一个StrutsActionProxy对象作为ActionProxy的默认实现。

还有一个关键是proxy调用的#prepare()方法,

    public void prepare() throws Exception {
        String profileKey = "create DefaultActionProxy: ";
        try {

            ......
            
            invocation = new DefaultActionInvocation(objectFactory, unknownHandler, this, extraContext, true, actionEventListener);
            resolveMethod();

        } finally {
            UtilTimerStack.pop(profileKey);
        }
    }


这里边创建了一个DefaultActionInvocation对象作为ActionInvocation对象的默认实现。下边又接着调用#resolveMethod()方法:

    private void resolveMethod() {
        if (!TextUtils.stringSet(this.method)) {
            this.method = config.getMethodName();
            if (!TextUtils.stringSet(this.method)) {
                this.method = "execute";
            }
        }
    }


这个方法实现了Action执行方法的设定,如果config中配置有方法名,那么就将这个方法名作为执行方法名,否则就用默认的execute。


(5) 构建好ActionProxy对象后,将request中的ValueStack更换成上面extraContext中设定的ValueStack。

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {

	......

        try {

	    ......

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

	    ......

    }



(6) 之后就要轮到执行Action了。

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {

	......

        try {

	    ......

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

	    if (mapping.getResult() != null) {
		Result result = mapping.getResult();
		result.execute(proxy.getInvocation());
	    } else {
		proxy.execute();
	    }

	    ......

    }


如果ActionMapping中的Result不是null,那么就直接跳转到Result。否则调用ActionProxy的#execute()。

    public String execute() throws Exception {
	......
        try {
	    ......

            retCode = invocation.invoke();
        } finally {
	    ......
        }

        return retCode;
    }


这里调用了DefaultActionInvocation的invoke()去实现Action的调用。

    public String invoke() throws Exception {
	......
    	try {
	        ......

    		if (interceptors.hasNext()) {// (1)
    			final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
    			UtilTimerStack.profile("interceptor: "+interceptor.getName(), 
    					new UtilTimerStack.ProfilingBlock<String>() {
							public String doProfiling() throws Exception {
				    			resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
				    			return null;
							}
    			});
    		} else {
    			resultCode = invokeActionOnly();
    		}

    		if (!executed) {// (2)
    			if (preResultListeners != null) {// (2)-1
    				for (Iterator iterator = preResultListeners.iterator();
    					iterator.hasNext();) {
    					PreResultListener listener = (PreResultListener) iterator.next();
    					
    					String _profileKey="preResultListener: ";
    					try {
    						UtilTimerStack.push(_profileKey);
    						listener.beforeResult(this, resultCode);
    					}
    					finally {
    						UtilTimerStack.pop(_profileKey);
    					}
    				}
    			}

    			if (proxy.getExecuteResult()) {// (2)-2
    				executeResult();
    			}

    			executed = true;
    		}

    		return resultCode;
    	}
    	finally {
    		UtilTimerStack.pop(profileKey);
    	}
    }



整个方法主要由2个if从句分割,在(1)处的if从句中,主要实现了拦截器的"递归"调用,说它是递归调用,其实是一种非传统的递归。传统的递归应该是函数调用自身,最后达成一定条件后退出,但是这里是将自身的引用作为参数传递给intercept(),然后在intercept()内部再调用DefaultActionInvocation的invoke(),实现了递归调用。

利用这种方式,实现了拦截器和Action的如下的调用逻辑:

Interceptor1
Interceptor2
Interceptor3
Action
Interceptor3
Interceptor2
Interceptor1

最后,当interceptors.hasNext()返回false时,也就是全部拦截器调用完毕之后,函数调用了invokeActionOnly();去实现Action的调用:

    public String invokeActionOnly() throws Exception {
    	return invokeAction(getAction(), proxy.getConfig());
    }


invokeActionOnly()内部是使用invokeAction()去实现Action的调用的,源代码如下:

    protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
        String methodName = proxy.getMethod();

        if (LOG.isDebugEnabled()) {
            LOG.debug("Executing action method = " + actionConfig.getMethodName());
        }

        String timerKey = "invokeAction: "+proxy.getActionName();
        try {
            UtilTimerStack.push(timerKey);
            
            Method method;
            try {
                method = getAction().getClass().getMethod(methodName, new Class[0]);
            } catch (NoSuchMethodException e) {
                try {
                    String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
                    method = getAction().getClass().getMethod(altMethodName, new Class[0]);
                } catch (NoSuchMethodException e1) {
                    throw e;
                }
            }

            Object methodResult = method.invoke(action, new Object[0]);
            if (methodResult instanceof Result) {
            	this.result = (Result) methodResult;
            	return null;
            } else {
            	return (String) methodResult;
            }
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
        } catch (InvocationTargetException e) {
            Throwable t = e.getTargetException();

            if (actionEventListener != null) {
                String result = actionEventListener.handleException(t, getStack());
                if (result != null) {
                    return result;
                }
            }
            if (t instanceof Exception) {
                throw(Exception) t;
            } else {
                throw e;
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }


由这句Object methodResult = method.invoke(action, new Object[0]);可以看出,最后通过反射实现了Action的执行方法的调用。


调用完方法之后,invoke()方法的流程来到了(2)处,由于刚刚调用完Action的那次invoke()调用此时executed为false,所以可以进入此处的if语句。
(2)-1处调用了在PreResultListener中的定义的一些执行Result前的操作。
(2)-2处则根据配置文件中的设置执行Result。

    private void executeResult() throws Exception {
        result = createResult();// 根据配置文件构建Result

        String timerKey = "executeResult: "+getResultCode();
        try {
            UtilTimerStack.push(timerKey);
            if (result != null) {
                result.execute(this);
            } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
                throw new ConfigurationException("No result defined for action " + getAction().getClass().getName() 
                        + " and result " + getResultCode(), proxy.getConfig());
            } else {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation());
                }
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }


于是,最终的调用顺序应该是:
Interceptor1
Interceptor2
Interceptor3
Action
PreResultListener
Result
Interceptor3
Interceptor2
Interceptor1
分享到:
评论
2 楼 bryan.liu 2009-05-06  
wmj2003 写道

分析的很好!很透彻!咨询一个问题,struts2是在什么时候将action的执行后的数据传递到request的attribute中去的?举例说明: action 中有查找学生记录的方法findStudents ,他将查找到的学生记录存放到了list中,那么这个list是在什么时候赋给request的attribute中去的?源代码是哪个java类?

我猜是在paramsInterceptor里面装载的,还在继续研究中。
1 楼 wmj2003 2008-09-21  
分析的很好!很透彻!咨询一个问题,struts2是在什么时候将action的执行后的数据传递到request的attribute中去的?举例说明: action 中有查找学生记录的方法findStudents ,他将查找到的学生记录存放到了list中,那么这个list是在什么时候赋给request的attribute中去的?源代码是哪个java类?

相关推荐

    struts 2 action 动态调用

    ### Struts 2 Action 动态方法调用详解 #### 一、引言 在Struts 2框架中,Action动态方法调用是一项非常实用的功能。它允许开发者在一个Action类中定义多个处理方法,而不仅仅局限于传统的`execute()`方法。这种...

    Struts2 动态方法调用(十三)

    在Struts2中,动态方法调用(Dynamic Method Invocation,DMI)是一种特性,允许我们通过URL直接调用Action类的方法,而无需在配置文件中显式指定。这在某些情况下提供了更大的灵活性。 在Struts2的动态方法调用中...

    struts2利用通配符调用同一个Action里面不同的方法

    在Struts2中,Action是业务逻辑处理的核心,而通配符的使用则是Struts2框架中一种灵活的配置方式,允许我们以更简洁的方式调用同一个Action中的不同方法。下面我们将深入探讨如何利用Struts2的通配符来实现这一功能...

    Struts2动态方法调用

    Struts2的配置文件(struts.xml)可以通过通配符来定义Action,使得一个Action能够处理多个方法调用。例如,我们可以定义一个Action,然后使用不同的参数值来调用不同的Action方法。这样可以减少Action的数量,简化...

    struts2 action的三种访问方式

    本篇文章将详细介绍Struts2 Action的三种访问方式:传统方式、通配符方式和动态方式。 1. **传统方式(Static Dispatch)** 传统方式是最基础的Action访问方式,通过硬编码URL来映射Action。在`struts.xml`配置...

    Struts2 Action参数详细说明

    默认情况下,Struts2会调用Action类中的execute方法来处理请求。但是,通过`method`属性,我们可以指定不同的方法对应不同的Action,这样可以实现一个类中多个方法的映射,提高代码复用性。例如: ```xml &lt;action ...

    struts2一个action处理多个请求 struts2实例

    在Struts2的配置文件(通常为struts.xml或struts.properties)中,我们定义Action的映射规则,包括请求路径、方法调用以及结果视图等。默认情况下,一个Action类对应一个请求URL,但通过特定配置,可以让一个Action...

    Struts2之异步调用机制详解案例struts014

    这个案例“struts014”很可能展示了如何在Struts2中实现异步调用来处理耗时操作,如数据库查询或复杂计算。 异步调用的基本概念是,客户端(通常是Web浏览器)发起一个请求,服务器不立即返回结果,而是启动一个...

    struts2 Action 包 命名空间 servlet API

    Struts2通过FilterDispatcher这个Servlet过滤器来拦截所有到达服务器的请求,判断是否需要由Struts2框架处理,如果是,则根据配置的Action映射调用相应的execute()方法。 Struts2的包结构清晰,每个包都有其特定的...

    Struts2 in action(struts2实战)

    - **Interceptor**(拦截器):拦截器是Struts2的核心组件,它们按照预定义的顺序对Action的调用进行拦截,实现如日志记录、权限验证、事务管理等功能。 2. **配置方式**: - **XML配置**:传统的Struts2配置通常...

    一个Action多方法调用的Struts 2的应用程序

    利用Struts 2框架创建一个web项目chap2_e22,实现用户登录过程。具体要求是在loginAction类中分别用login()和registered()处理用户登录和注册的过程,分别创建login.jsp和register.jsp两个页面实现登录和注册的...

    Struts 2实战 Struts 2 in action 的中文版

    根据提供的信息,我们可以推断出这是一本关于Struts 2框架的书籍——《Struts 2实战 Struts 2 in action 的中文版》。本书主要介绍了Struts 2框架的相关概念、工作原理以及实际应用案例等内容。接下来,我们将根据...

    struts2笔记之动态调用Action指定方法及默认Action

    Struts2 动态调用 Action 指定方法及默认 Action 配置 Struts2 框架中,一个 Action 可以包含多个处理逻辑,而不是只有一个 execute() 方法。在实际开发中,我们经常需要在一个 Action 中实现多个处理逻辑,这样...

    JS调用Struts中的Action

    ### JS调用Struts中的Action #### 背景与概念 在Web开发中,JavaScript(简称JS)作为客户端脚本语言,常被用来增强用户体验、处理表单验证等前端任务。而Struts框架则是Java Web开发中常用的一个MVC(Model-View-...

    struts2动态调用之通配符

    Struts2会根据URL中的部分替换通配符,并尝试调用Action类中的方法。如果找不到与之匹配的方法,Struts2将会回退到默认的执行方法(通常是`execute`)。 为了更高效地利用这一特性,我们需要遵循以下几点: 1. 方法...

    Struts2 in action中文版+配套源代码

    2. **拦截器(Interceptors)**:Struts2的拦截器机制允许开发者定义一系列处理请求的规则,如日志记录、权限检查、事务管理等,这些规则可以在Action执行前后被调用,提高了代码的复用性和模块化。 3. **结果类型...

    ajaxt json 调用struts2 action的实例(myeclipse 直接导入运行)

    ajaxt json 调用struts2 action的实例(myeclipse 直接导入运行) 学习点: 1;怎样在页面用ajax调用struts2的action 2;怎样对struts进行配置 3;ajax的运行历程 最简单明了的实例,清晰的帮你弄清上述概念,运行...

    Struts2接口文档

    而Interceptor接口则用于在Action调用前后插入自定义逻辑,例如日志记录、权限检查等。 在Struts2的配置中,Struts.xml文件扮演着关键角色,它包含了Action、Result、Interceptor的配置信息。通过这个配置文件,...

    Struts2 in action 中文版.pdf

    《Struts2 in Action》是一本深入探讨Struts2框架的权威著作,中文版的发布使得国内开发者能够更方便地理解和应用这一强大的Java Web开发框架。Struts2是Apache软件基金会旗下的一个开源项目,它是MVC(Model-View-...

    Struts2SpringUnitDemo单元测试

    4. **Action和Service的测试**:在Struts2中,Action类是处理用户请求的实体,它通常调用Service层来执行业务逻辑。Service层封装了与数据库或其他外部资源的交互。在单元测试中,Action的测试主要检查其接收请求、...

Global site tag (gtag.js) - Google Analytics