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的原理图:
- Struts2核心(一):执行线路与拦截器
- Struts2核心拦截器
- Struts2核心--拦截器
- struts2执行流程与拦截器介绍
- struts2-拦截器(一)
- Struts2 拦截器(一)
- Struts2(五) 核心拦截器
- struts2核心与拦截器的原理解析
- Struts2拦截器(一)
- struts2拦截器(一)
- Struts2拦截器执行顺序
- Struts2拦截器执行顺序
- Struts2拦截器执行顺序
- Struts2拦截器执行顺序
- Struts2拦截器执行顺序
- Struts2拦截器执行顺序
- Struts2拦截器执行顺序
- Struts2拦截器执行顺序
- 20150129 百度Ueditor 上传本地图片失败的处理
- Oracle 数据恢复
- Java中的递归
- 部署有用,先记录下以免忘记
- WOJ-Problem 1004 - Noah's Ark
- Struts2核心(一):执行线路与拦截器
- 单链表
- 基於數據挖掘進行資料庫營銷
- poj C Looooops
- How to Call Java Functions from C Using JNI
- 静默安装Oracle11gR2 [FATAL] [INS-32015]报错
- Linux驱动之make menuconfig make uImage Kconfig
- poj 2756 Autumn is a Genius 高精度加减
- Ubuntu 12.04 多机并行 vasp 集群