Struts2核心(一):执行线路与拦截器

来源:互联网 发布:ubuntu 16.04安装视频 编辑:程序博客网 时间:2024/05/02 02:45

要使用Sruts2,需要注册一个过滤器:StrutsPrepareAndExecuteFilter。FilterDispatcher自2.1.3以后已过时。

注:以下的Sruts都指Sruts2。

既然StrutsPrepareAndExecuteFilter是Struts的执行入口,那就从它开始分析。

StrutsPrepareAndExecuteFilter类有三个成员变量:

protected PrepareOperations prepare;    protected ExecuteOperations execute;protected List<Pattern> excludedPatterns = null;
三个成员变量都会在初始化方法(只会执行一次)中实例化:
public void init(FilterConfig filterConfig) throws ServletException {        InitOperations init = new InitOperations();        try {            FilterHostConfig config = new FilterHostConfig(filterConfig);            init.initLogging(config);            Dispatcher dispatcher = init.initDispatcher(config);            init.initStaticContentLoader(config, dispatcher);            prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);            execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);            postInit(dispatcher, filterConfig);        } finally {            init.cleanup();        }    }
PrepareOperations类主要是为Action的执行做准备,通过它的findActionMapping()方法可以返回一个ActionMapping对象。

ExecuteOperations类通过ActionMapping来执行Action。

初始化方法中会产生一个Dispatcher对象,并且将其传递给PrepareOperations和ExecuteOperations。该对象会在后面用到。

excludedPatterns则是URL过滤,被过滤掉的URL不会被Struts执行。

接下来就是过滤器的执行方法doFilter()(每一次请求都会执行):

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        try {            prepare.setEncodingAndLocale(request, response);            prepare.createActionContext(request, response);            prepare.assignDispatcherToThread();if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {chain.doFilter(request, response);} else {request = prepare.wrapRequest(request);ActionMapping mapping = prepare.findActionMapping(request, response, true);if (mapping == null) {boolean handled = execute.executeStaticResourceRequest(request, response);if (!handled) {chain.doFilter(request, response);}} else {execute.executeAction(request, response, mapping);}}        } finally {            prepare.cleanupRequest(request);        }    }
先判断URL是否被过滤:如果是,则跳到下一个过滤器;如果不是,则通过prepare找到ActionMapping。如果没有找到ActionMapping,则把当前的URL当作静态资源处理;如果找到ActionMapping,则执行Action。

接下来执行execute.executeAction()方法:

public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {        dispatcher.serviceAction(request, response, servletContext, mapping);    }
该方法调用了dispatcher.serviceAction()方法:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,                              ActionMapping mapping) throws ServletException {        Map<String, Object> extraContext = createContextMap(request, response, mapping, context);        // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);        boolean nullStack = stack == null;        if (nullStack) {            ActionContext ctx = ActionContext.getContext();            if (ctx != null) {                stack = ctx.getValueStack();            }        }        if (stack != null) {            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.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();            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!            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);            }        } catch (ConfigurationException e) {        // WW-2874 Only log error if in devMode            if (devMode) {                String reqStr = request.getRequestURI();                if (request.getQueryString() != null) {                    reqStr = reqStr + "?" + request.getQueryString();                }                LOG.error("Could not find action or result\n" + reqStr, e);            } else {                if (LOG.isWarnEnabled()) {                    LOG.warn("Could not find action or result", e);                }            }            sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);        } catch (Exception e) {            if (handleException || devMode) {                sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);            } else {                throw new ServletException(e);            }        } finally {            UtilTimerStack.pop(timerKey);        }    }
这个方法比较长,但是我们只关心下面一小段代码:
String namespace = mapping.getNamespace();            String name = mapping.getName();            String method = mapping.getMethod();            Configuration config = configurationManager.getConfiguration();            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!            if (mapping.getResult() != null) {                Result result = mapping.getResult();                result.execute(proxy.getInvocation());            } else {                proxy.execute();            }

这段代码根据传入的ActionMapping,创建了一个ActionProxy,然后调用了proxy.execute()方法。

ActionProxy的创建使用了工厂模式,工厂接口是ActionProxyFactory,Struts提供的实现类是StrutsActionProxyFactory。

这里调用的工厂方法是StrutsActionProxyFactory父类DefaultActionProxyFactory的方法:

public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {                ActionInvocation inv = new DefaultActionInvocation(extraContext, true);        container.inject(inv);        return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);    }
在创建ActionProxy对象之前先创建了ActionInvocation对象。接着回调了子类StrutsActionProxyFactory的工厂方法:

 public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {                StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);        container.inject(proxy);        proxy.prepare();        return proxy;    }
可以看到ActionProxy的实现类是StrutsActionProxy。在创建Proxy的时候传入了先前创建好的Invocation对象。在创建ActionProxy实例后,调用proxy.prepare()方法做一些准备工作:
protected void prepare() {        super.prepare();    }
调用了父类的prepare()方法,StrutsActionProxy的父类是DefaultActionProxy。看一下DefaultActionProxy.prepare()方法:
protected void prepare() {        String profileKey = "create DefaultActionProxy: ";        try {            UtilTimerStack.push(profileKey);            config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);            if (config == null && unknownHandlerManager.hasUnknownHandlers()) {                config = unknownHandlerManager.handleUnknownAction(namespace, actionName);            }            if (config == null) {                throw new ConfigurationException(getErrorMessage());            }            resolveMethod();            if (!config.isAllowedMethod(method)) {                throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);            }            invocation.init(this);        } finally {            UtilTimerStack.pop(profileKey);        }    }
在这里调用了invocation.init()方法初始化invocation(invocation是在创建proxy的时候传给proxy的)。invocation.init()方法:
public void init(ActionProxy proxy) {        this.proxy = proxy;        Map<String, Object> contextMap = createContextMap();        // Setting this so that other classes, like object factories, can use the ActionProxy and other        // contextual information to operate        ActionContext actionContext = ActionContext.getContext();        if (actionContext != null) {            actionContext.setActionInvocation(this);        }        createAction(contextMap);        if (pushAction) {            stack.push(action);            contextMap.put("action", action);        }        invocationContext = new ActionContext(contextMap);        invocationContext.setName(proxy.getActionName());        // get a new List so we don't get problems with the iterator if someone changes the list        List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());        interceptors = interceptorList.iterator();    }
在这里把proxy传给了invocation,可见proxy与invocation是一对一的关系,它们相互引用。这个方法干了两件重要的事:创建Action实例和获取Action的拦截器。

1.创建Action实例createAction():

protected void createAction(Map<String, Object> contextMap) {        // load action        String timerKey = "actionCreate: " + proxy.getActionName();        try {            UtilTimerStack.push(timerKey);            action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);        } catch (InstantiationException e) {            throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());        } catch (IllegalAccessException e) {            throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());        } 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, proxy.getConfig());        } finally {            UtilTimerStack.pop(timerKey);        }        if (actionEventListener != null) {            action = actionEventListener.prepare(action, stack);        }    }
从上面的代码可以看到,struts的Action实例都是由ObjectFactory创建的,ObjectFactory是可以配置的,struts集成spring就是在这个基础上完成的。

2.获取拦截器:

 List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());        interceptors = interceptorList.iterator();
返回的拦截器是放在迭代器里面的,这对struts实现拦截器机制至关重要,后面会看到。
至此,ActionPorxy和ActionInvocation创建完毕!现在回到dispatcher.serviceAction()方法中,在dispatcher.serviceAction()方法中调用了proxy.execute()方法(proxy的实现类是StrutsActionProxy)开始正式执行Action:

public String execute() throws Exception {        ActionContext previous = ActionContext.getContext();        ActionContext.setContext(invocation.getInvocationContext());        try {// This is for the new API://            return RequestContextImpl.callInContext(invocation, new Callable<String>() {//                public String call() throws Exception {//                    return invocation.invoke();//                }//            });            return invocation.invoke();        } finally {            if (cleanupContext)                ActionContext.setContext(previous);        }    }
这里调用了ActionInvocation的invoke()方法(实现类是DefaultActionInvocation),invoke方法做了下面几件事。

1.调用拦截器:

if (interceptors.hasNext()) {                final InterceptorMapping interceptor = interceptors.next();                String interceptorMsg = "interceptor: " + interceptor.getName();                UtilTimerStack.push(interceptorMsg);                try {                                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);                            }                finally {                    UtilTimerStack.pop(interceptorMsg);                }            } else {                resultCode = invokeActionOnly();            }
我们一般会在拦截器里面再次调用ActionInvocation.invoke()方法,所以ActionInvocation.invoke()会被递归调用,直到所有的拦截器都被调用(但是此时所有的拦截器都还没有执行完毕!)

2.当迭代器迭代完毕后(所有的拦截器都被调用),会调用真正执行Action的方法invokeActionOnly():

public String invokeActionOnly() throws Exception {        return invokeAction(getAction(), proxy.getConfig());    }
invokeAction(Object action, ActionConfig actionConfig):
UtilTimerStack.push(timerKey);            boolean methodCalled = false;            Object methodResult = null;            Method method = null;            try {                method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);            } catch (NoSuchMethodException e) {                // hmm -- OK, try doXxx instead                try {                    String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);                    method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);                } catch (NoSuchMethodException e1) {                    // well, give the unknown handler a shot                    if (unknownHandlerManager.hasUnknownHandlers()) {                        try {                            methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);                            methodCalled = true;                        } catch (NoSuchMethodException e2) {                            // throw the original one                            throw e;                        }                    } else {                        throw e;                    }                }            }            if (!methodCalled) {                methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);//执行Action方法,也就是我们在Action中定义的并且在配置文件中配置的方法。            }            return saveResult(actionConfig, methodResult);
保存返回的结果saveResult(ActionConfig actionConfig, Object methodResult):
protected String saveResult(ActionConfig actionConfig, Object methodResult) {        if (methodResult instanceof Result) {            this.explicitResult = (Result) methodResult;            // Wire the result automatically            container.inject(explicitResult);            return null;        } else {            return (String) methodResult;        }    }
很显然,我们定义的action返回的值常常是String或者void类型的。

3.处理结果:

// this is needed because the result will be executed, then control will return to the Interceptor, which will            // return above and flow through again            if (!executed) {                if (preResultListeners != null) {                    for (Object preResultListener : preResultListeners) {                        PreResultListener listener = (PreResultListener) preResultListener;                        String _profileKey = "preResultListener: ";                        try {                            UtilTimerStack.push(_profileKey);                            listener.beforeResult(this, resultCode);                        }                        finally {                            UtilTimerStack.pop(_profileKey);                        }                    }                }                // now execute the result, if we're supposed to                if (proxy.getExecuteResult()) {                    executeResult();                }                executed = true;            }
executeResult():
private void executeResult() throws Exception {        result = createResult();        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);        }    }
在该方法中会先创建一个结果result对象createResult():

public Result createResult() throws Exception {        if (explicitResult != null) {            Result ret = explicitResult;            explicitResult = null;            return ret;        }        ActionConfig config = proxy.getConfig();        Map<String, ResultConfig> results = config.getResults();        ResultConfig resultConfig = null;        try {            resultConfig = results.get(resultCode);        } catch (NullPointerException e) {            // swallow        }                if (resultConfig == null) {            // If no result is found for the given resultCode, try to get a wildcard '*' match.            resultConfig = results.get("*");        }        if (resultConfig != null) {            try {                return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());            } catch (Exception e) {                LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);                throw new XWorkException(e, resultConfig);            }        } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {            return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);        }        return null;    }
通过前面的分析,explicitResult并没有设置,所以会执行下面的代码。由下面的代码可知,result对象也是由objectFactory创建的。

result是我们在配置文件通过result-type配置的,所有的result都实现了接口com.opensymphony.xwork2.Result。默认的result-type是dispatcher,实现类是ServletDispatcherResult。

创建完result对象后,会执行result.execute()方法。如果配置的result-type是dispatcher(默认的),则ServletDispatcherResult类的doExecute()方法会被调用:

public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {        if (LOG.isDebugEnabled()) {            LOG.debug("Forwarding to location " + finalLocation);        }        PageContext pageContext = ServletActionContext.getPageContext();        if (pageContext != null) {            pageContext.include(finalLocation);        } else {            HttpServletRequest request = ServletActionContext.getRequest();            HttpServletResponse response = ServletActionContext.getResponse();            RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);            //add parameters passed on the location to #parameters            // see WW-2120            if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {                String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);                Map<String, Object> parameters = getParameters(invocation);                Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true);                if (queryParams != null && !queryParams.isEmpty())                    parameters.putAll(queryParams);            }            // if the view doesn't exist, let's do a 404            if (dispatcher == null) {                response.sendError(404, "result '" + finalLocation + "' not found");                return;            }            //if we are inside an action tag, we always need to do an include            Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE);            // If we're included, then include the view            // Otherwise do forward            // This allow the page to, for example, set content type            if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {                request.setAttribute("struts.view_uri", finalLocation);                request.setAttribute("struts.request_uri", request.getRequestURI());                dispatcher.forward(request, response);            } else {                dispatcher.include(request, response);            }        }    }
实际上就是调用了javax.servlet.RequestDispatcher.forward()方法。

由于是递归调用,此时拦截器还没有调用完毕。结果处理完后,会以相反的顺序调用拦截器

至此,一个Action请求大致执行完毕!我们可以看到,执行Action的核心方法是ActionInvocation.invoke()。

最后附上struts2的原理图:






0 0
原创粉丝点击