WebWork执行流程

来源:互联网 发布:韩子高网络剧百度云 编辑:程序博客网 时间:2024/05/01 18:04

com.opensymphony.webwork下有两个类:public class ServletActionContext extends ActionContext implements WebWorkStatics;
这个类是静态工具类,其内的方法均依赖com.opensymphony.xwork.ActionContext,而这个类提供了Action类的执行上下文.我们跟踪这个上下文的生命周期:

客户请求发送到Webserver,它将请求转发到Servlet容器,容器匹配一个Sertlet,然后调用其sevice()方法.在WebWork中这个servlet是ServletDispatcher,其中的serviceAction方法通过代理模式从WebWork进入到XWork对象世界,在那里对客户请求数据对象执行处理,通过执行根据配置上下文和执行上下文设置的Action和result把响应数据发回客户端,发回的动作可能发生在Result中,也可能发生在Interceptor中.开始代码之旅.

下面是serviceAction代码:

public void serviceAction(HttpServletRequest request, HttpServletResponse response, String namespace, String actionName, Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap) {
        HashMap extraContext = createContextMap(requestMap, parameterMap, sessionMap, applicationMap, request, response, getServletConfig());

        extraContext.put(SERVLET_DISPATCHER, this);

        // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
        OgnlValueStack stack = (OgnlValueStack) request.getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY);
        if (stack != null) {
            extraContext.put(ActionContext.VALUE_STACK,new OgnlValueStack(stack));
        }
        try {
            ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);
            request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack());
            proxy.execute();
            // If there was a previous value stack then set it back onto the request
            if (stack != null){
                request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY,stack);
            }
        } catch (ConfigurationException e) {
            log.error("Could not find action", e);
            sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
        } catch (Exception e) {
            log.error("Could not execute action", e);
            sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
        }
    }

方法createContextMap执行数据的包装:

    public static HashMap createContextMap(Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap, HttpServletRequest request, HttpServletResponse response, ServletConfig servletConfig) {
        HashMap extraContext = new HashMap();
        extraContext.put(ActionContext.PARAMETERS, parameterMap);
        extraContext.put(ActionContext.SESSION, sessionMap);
        extraContext.put(ActionContext.APPLICATION, applicationMap);
        extraContext.put(ActionContext.LOCALE, (locale == null) ? request.getLocale() : locale);

        extraContext.put(HTTP_REQUEST, request);
        extraContext.put(HTTP_RESPONSE, response);
        extraContext.put(SERVLET_CONFIG, servletConfig);
        extraContext.put(ComponentInterceptor.COMPONENT_MANAGER, request.getAttribute(ComponentManager.COMPONENT_MANAGER_KEY));

        // helpers to get access to request/session/application scope
        extraContext.put("request", requestMap);
        extraContext.put("session", sessionMap);
        extraContext.put("application", applicationMap);
        extraContext.put("parameters", parameterMap);

        AttributeMap attrMap = new AttributeMap(extraContext);
        extraContext.put("attr", attrMap);

        return extraContext;
    }


众所周知,不仅WebWork能使用XWork,而且还可以有类似的FtpWork,MailWork,JMSWork能够替代WebWork来使用Xwork,为什么能够这样,因为Xwork的使用方式.在上面的serviceAction方法中:

 ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);
            request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack());
            proxy.execute();
这些代码就是这种方式的体现:动作代理-动作调用-具体的动作.为使客户代码不绑定到特定代理,XWork使用了一个工厂模式,提供工厂类 ActionProxyFactory.

方法createActionProxy实际调用导致一个ActionProxy接口的缺省实现DefaultActionProxy被实例化:

它的构造器是:

    protected ActionInvocation invocation;

    protected DefaultActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult) throws Exception {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating an DefaultActionProxy for namespace " + namespace + " and action name " + actionName);
        }

        this.actionName = actionName;
        this.namespace = namespace;
        this.executeResult = executeResult;
        this.extraContext = extraContext;

        config = ConfigurationManager.getConfiguration().getRuntimeConfiguration().getActionConfig(namespace, actionName);

        if (config == null) {
            String message;

throw new ConfigurationException(message);
        }

        prepare();
    }

    protected void prepare() throws Exception {

        //创建ActionInvocation 实例化的仍然是由代理工厂类的工厂方法进行,实例化该接口的缺省         实现DefaultActionInvocation.

        invocation = ActionProxyFactory.getFactory().createActionInvocation(this, extraContext);
    }

下面是proxy.execute()的细节:

    public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        ActionContext.setContext(invocation.getInvocationContext());

        String retCode = null;

        try {

            retCode = invocation.invoke();
        } finally {

            ActionContext.setContext(nestedContext);
        }

        return retCode;

    }

最后,看看这个类: DefaultActionInvocation类的invoke方法是如何处理extraContext,先看初始化过程,从构造器开始:

    protected DefaultActionInvocation(ActionProxy proxy, Map extraContext, boolean pushAction) throws Exception {

        this.proxy = proxy;

        this.extraContext = extraContext;

        this.pushAction = pushAction;

        init();

}

方法init定义,extraContext转换到invocationContext,并获得拦截器清单.

    private void init() throws Exception {

        Map contextMap = createContextMap();

        createAction();

        if (pushAction) {

            stack.push(action);

        }

        invocationContext = new ActionContext(contextMap);

        invocationContext.setName(proxy.getActionName());

        List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());

        interceptors = interceptorList.iterator();

}

在下面这个方法中获得动作上下文contextMap,并把extraContext包含其中.

    protected Map createContextMap() {

        Map contextMap;

    if ((extraContext!=null)&& (extraContext.containsKey(ActionContext.VALUE_STACK))) {

            // In case the ValueStack was passed in

            stack = (OgnlValueStack) extraContext.get(ActionContext.VALUE_STACK);

            if (stack == null) {

                throw new IllegalStateException("There was a null Stack set into the extra params.");

            }

            contextMap = stack.getContext();

        } else {

            // create the value stack

            // this also adds the ValueStack to its context

            stack = new OgnlValueStack();

            // create the action context

            contextMap = stack.getContext();

        }

        // put extraContext in

        if (extraContext != null) {

            contextMap.putAll(extraContext);

        }

        //put this DefaultActionInvocation into the context map

        contextMap.put(ActionContext.ACTION_INVOCATION, this);

        return contextMap;

    }

    protected void createAction() {

        // load action

        try {

            action = ObjectFactory.getObjectFactory().buildAction(proxy.getConfig());

        } catch (InstantiationException e) {

            throw new XworkException("Unable to intantiate Action!", e);

        } catch (IllegalAccessException e) {

            throw new XworkException("Illegal access to constructor, is it public?", e);

        } catch (ClassCastException e) {

            throw new XworkException("Action class " + proxy.getConfig().getClassName() + " does not implement " + Action.class.getName(), e);

        } catch (Exception e) {

            String gripe = "";

            if (proxy == null) {

                gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";

            } else if (proxy.getConfig() == null) {

                gripe = "Sheesh.  Where'd that ActionProxy get to?  I can't find it in the current ActionInvocation!?";

            } else if (proxy.getConfig().getClassName() == null) {

                gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";

            } else {

                gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";

            }

            gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");

            throw new XworkException(gripe, e);

        }

    }

初始化结束了, 接下来看看invocation.invoke()到底做了什么:

    public String invoke() throws Exception {

        if (executed) {

            throw new IllegalStateException("Action has already executed");

        }

        if (interceptors.hasNext()) {

            Interceptor interceptor = (Interceptor) interceptors.next();

                     在这个类中,并没有调用getResult(),因为这个方法是由某些拦截器调用的,例如: ExecuteAndWaitInterceptor, TokenSessionStoreInterceptor,如果自己需要,也可以创建自己的拦截器,并在其中访问它.

通过Interceptor接口中的String intercept(ActionInvocation invocation) throws Exception可以看到, 参数invocation已经携带了所有能处理的数据了.

            resultCode = interceptor.intercept(this);

        } else {

            resultCode = invokeAction(getAction(), proxy.getConfig());

        }

        if (!executed) {

            if (preResultListeners != null) {

                for (Iterator iterator = preResultListeners.iterator();

                     iterator.hasNext();) {

                    PreResultListener listener = (PreResultListener) iterator.next();

                    listener.beforeResult(this, resultCode);

                }

            }

            now execute the result, if we're supposed to,如果不期望执行Result,那么在代理中设置proxy.getExecuteResult(false)即可,有时候是需要这样的,TokenSessionStoreInterceptor;

            if (proxy.getExecuteResult()) {

                executeResult();

            }

            executed = true;

        }

        return resultCode;

}

在这个方法中,调用了Result接口的方法execute(ActionInvocation),当该方法返回时一个请求响应回合就完成了.

private void executeResult() throws Exception {

        result = createResult();

        if (result != null) {

            result.execute(this);

        } else if (!Action.NONE.equals(resultCode)) {

            LOG.warn("No result defined for action " + getAction().getClass().getName() + " and result " + getResultCode());

        }

    }

public Result createResult() throws Exception {

        Map results = proxy.getConfig().getResults();

        ResultConfig resultConfig = (ResultConfig) results.get(resultCode);

        Result newResult = null;

        if (resultConfig != null) {

            try {

                newResult = ObjectFactory.getObjectFactory().buildResult(resultConfig);

            } catch (Exception e) {

                LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);

                throw e;

            }

        }

        return newResult;

}

public Result getResult() throws Exception {

        Result returnResult = result;

        // If we've chained to other Actions, we need to find the last result

        while (returnResult instanceof ActionChainResult) {

            ActionProxy aProxy = ((ActionChainResult) returnResult).getProxy();

            if (aProxy != null) {

                Result proxyResult = aProxy.getInvocation().getResult();

                if ((proxyResult != null) && (aProxy.getExecuteResult())) {

                    returnResult = proxyResult;

                } else {

                    break;

                }

            } else {

                break;

            }

        }

        return returnResult;

}

protected String invokeAction(Action action, ActionConfig actionConfig) throws Exception {

        if (proxy.getConfig().getMethodName() == null) {

            return getAction().execute();

        } else {

            if (LOG.isDebugEnabled()) {

                LOG.debug("Executing action method = " + actionConfig.getMethodName());

            }

            try {

                Method method = actionConfig.getMethod(action.getClass());

                if (action instanceof Proxy) {

                    try {

                        return (String) Proxy.getInvocationHandler(action).invoke(action, method, new Object[0]);

                    } catch (Throwable throwable) {

                        throwable.printStackTrace();

                        throw new Exception("Error invoking on proxy: " + throwable.getMessage());

                    }

                } else {

                    return (String) method.invoke(action, new Object[0]);

                }

            } catch (NoSuchMethodException e) {

                throw new IllegalArgumentException("Method '" + actionConfig.getMethodName() + "()' is not defined in action '" + getAction().getClass() + "'");

            } catch (InvocationTargetException e) {

                // We try to return the source exception.

                Throwable t = e.getTargetException();

                if (t instanceof Exception) {

                    throw (Exception) t;

                } else {

                    throw e;

                }

            }

        }

    }

}