struts2 处理请求流程分析(结合源码)
http://zhxing.iteye.com/blog/541059
struts2 源碼版本2.0.11.1
本文是綜合網上部分人的分析成果,然后再自己結合源碼進行的,分析中如有錯誤,請指正。
?? 從struts2 中的web.xml的啟動配置可以看出,首先分析的是FilterDispatcher 這個過濾器類。
?
1、過濾器的初始化方法 void init(FilterConfig filterConfig)
Java代碼 //初始化方法 public void init(FilterConfig filterConfig) throws ServletException {this.filterConfig = filterConfig;//獲得默認的參數,創建dispathcher 對象dispatcher = createDispatcher(filterConfig);dispatcher.init();String param = filterConfig.getInitParameter("packages");String packages = "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";if (param != null) {packages = param + " " + packages;}this.pathPrefixes = parse(packages);}?
??? 1.1、createDispatcher(filterConfig);方法,該方法的目的是創建Dispathcher 對象
?
Java代碼 protected Dispatcher createDispatcher(FilterConfig filterConfig) {//讀取相應過濾器的web.xml 配置Map<String,String> params = new HashMap<String,String>();for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements(); ) {String name = (String) e.nextElement();String value = filterConfig.getInitParameter(name);params.put(name, value);}//可以看出Dispatcher 類包裝了ServletContext 和過濾器的web.xml 配置return new Dispatcher(filterConfig.getServletContext(), params);}?
?
? 1.2、dispatcher.init();方法,該方法對dispatcher進行了一系列的初始化工作,這個工作很重要也有點復雜,具體每個初始化的工作的流程怎樣,待有空閑的時候再繼續分析,網上也有人已經分析過了,如果有興趣可參照:http://zddava.iteye.com/blog/211795
?
Java代碼 public void init() {if (configurationManager == null) {configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);}//讀取properties信息,默認的default.propertiesinit_DefaultProperties(); // [1]//讀取xml配置文件,默認的struts-default.xml,struts-plugin.xml,struts.xmlinit_TraditionalXmlConfigurations(); // [2]//讀取用戶自定義的struts.properties init_LegacyStrutsProperties(); // [3]//讀取FilterDispatcher的配置中所定義的actionPackages屬性,傳說中的Struts 2 零配置所謂的零配置init_ZeroConfiguration(); // [4]//自定義的configProviders init_CustomConfigurationProviders(); // [5]//該功能全面被注釋init_MethodConfigurationProvider();//載入FilterDispatcher傳進來的initParams init_FilterInitParameters() ; // [6]//將配置文件中的bean與具體的類映射 init_AliasStandardObjects() ; // [7]//構建一個用于依賴注射的Container對象 //在這里面會循環調用上面七個ConfigurationProvider的register方法 //其中的重點就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration();init_CheckConfigurationReloading(container);init_CheckWebLogicWorkaround(container);}?
?? 1.3、String param = filterConfig.getInitParameter("packages"); 以下的代碼。這個步驟載入了packages標簽下定義的靜態資源。 讀取web.xml中 的下面的配置路徑還有org.apache.struts2.static,template,org.apache.struts2.interceptor.debugging這三個包空間下邊的資源也會作為靜態資源載入。
Xml代碼 <filter><filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class><init-param><param-name>packages</param-name><param-value>cn.static.resource</param-value></init-param></filter>?
? ? 1.4、this.pathPrefixes = parse(packages);這個步驟是對packages 進行解析的。
Java代碼 protected String[] parse(String packages) {if (packages == null) {return null;}List<String> pathPrefixes = new ArrayList<String>();StringTokenizer st = new StringTokenizer(packages, ", \n\t");while (st.hasMoreTokens()) {String pathPrefix = st.nextToken().replace('.', '/');if (!pathPrefix.endsWith("/")) {pathPrefix += "/";}pathPrefixes.add(pathPrefix);}return pathPrefixes.toArray(new String[pathPrefixes.size()]);}?
?
2、過濾器中的doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 方法
?
?
?
2.1、request = prepareDispatcherAndWrapRequest(request, response);分析
???? 我們知道JSTL默認是從page,request,session,application這四個Scope逐次查找相應的EL表達式所對應的對象的值。那么如果要使用JSTL來讀取Action中的變量,就需要把Action中的變量,放到request域中才行Struts2,都使用另外一種整合方式:對HttpServletRequest進行裝飾(StrutsRequestWrapper )這個類會在Struts2初始化的時候,替換HttpServletRequest,運行于整個Struts2的運行過程中,當我們試圖調用request.getAttribute()的時候,就會執行上面的這個方法。(這是一個典型的裝飾器模式)在執行上面的方法時,會首先調用HttpServletRequest中原本的request.getAttribute(),如果沒有找到,它會繼續到ValueStack中去查找,而action在ValueStack中,所以action中的變量通過OGNL表達式,就能找到對應的值了。
?
Java代碼 protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException {//獲取dispatch 的單例類,是由LocalThread 保存的,保證線程的安全Dispatcher du = Dispatcher.getInstance();// Prepare and wrap the request if the cleanup filter hasn't already,// cleanup filter should be// configured first before struts2 dispatcher filter, hence when its// cleanup filter's turn,// static instance of Dispatcher should be null.if (du == null) {//如果為空的話,值保存進LocalThread 中Dispatcher.setInstance(dispatcher);// prepare the request no matter what - this ensures that the proper// character encoding// is used before invoking the mapper (see WW-9127)// request 編碼設置和response 本地化設置dispatcher.prepare(request, response);} else {dispatcher = du;}try {// Wrap request first, just in case it is multipart/form-data// parameters might not be accessible through before encoding// (ww-1278)//在這里就開始包裝request = dispatcher.wrapRequest(request, getServletContext());} catch (IOException e) {String message = "Could not wrap servlet request with MultipartRequestWrapper!";LOG.error(message, e);throw new ServletException(message, e);}return request;}?
?簡單看下這個方法Dispatcher.getInstance();
Java代碼 private static ThreadLocal<Dispatcher> instance = new ThreadLocal<Dispatcher>(); //other code public static Dispatcher getInstance() {return instance.get(); }?
主要的包裝在此方法進行request = dispatcher.wrapRequest(request, getServletContext());
Java代碼 public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {// don't wrap more than once 如果已經包裝了,就不用再包裝了if (request instanceof StrutsRequestWrapper) {return request;}String content_type = request.getContentType();//非表單提交的request 封裝,主要是圖片上傳等 if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {MultiPartRequest multi = getContainer().getInstance(MultiPartRequest.class);//如果是非表單提交則包裝成MultiPartRequestWrapperrequest = new MultiPartRequestWrapper(multi, request, getSaveDir(servletContext));} else {//如果是普通表單提交,在此包裝request = new StrutsRequestWrapper(request);}return request;}?
2.2、mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());方法的分析
?? 這里的分析參照了:http://zddava.iteye.com/blog/215504
下面來看一下默認使用的ActionMapper實現DefaultActionMapper的#getMapping():
?
主要有6處需要重點說明:
(1) 關于ActionMapping類,它內部封裝了如下5個字段:
?
這些在配置文件中都是可設置的,確定了ActionMapping類的各個字段的值,就可以對請求的Action進行調用了。
(2) String uri = getUri(request);
這個步驟用于獲取客戶端發送的請求的URI,源代碼如下:
?
???? 這個方法首先判斷請求是否來自于一個jsp的include,如果是,那么請求的"javax.servlet.include.servlet_path"屬性可以獲得include的頁面uri,否則通過一般的方法獲得請求的uri,最后返回去掉ContextPath的請求路徑,比如http://127.0.0.1:8087/test/jsp/index.jsp?param=1,返回的為/jsp/index.jsp。去掉了ContextPath和查詢字符串等。
(3) uri = dropExtension(uri); 負責去掉Action的"擴展名"(默認為"action"),源代碼如下:
?
Java代碼 String dropExtension(String name) {//extensions 為struts2 的后綴名,可有多個,默認為action// List extensions = new ArrayList() {{ add("action");}};if (extensions == null) {return name;}Iterator it = extensions.iterator();//分別遍歷后去掉后綴名while (it.hasNext()) {String extension = "." + (String) it.next();if (name.endsWith(extension)) {name = name.substring(0, name.length() - extension.length());return name;}}return null;}?
注意,這個步驟對于不是以特地擴展名結尾的請求會返回一個null的uri,進而#getMapping()也會返回null,FilterDispatcher的#doFilter()就會把這次請求當作一個普通請求對待了。
(4) parseNameAndNamespace(uri, mapping, configManager);
此方法用于解析Action的名稱和命名空間,并賦給ActionMapping對象。源代碼如下:
?
(5) handleSpecialParameters(request, mapping); 此方法用于處理Struts框架定義的四種特殊的prefix:
下邊是struts2的javadoc里提供的例子:
Method prefix:調用baz的另外一個方法"anotherMethod"而不是"execute"
?
Action prefix:調用anotherAction的"execute"
Html代碼 <a:form action="baz"><a:textfield label="Enter your name" name="person.name"/><a:submit value="Create person"/><a:submit name="action:anotherAction" value="Cancel"/></a:form>?
Redirect prefix:將請求重定向,下例中為定向到google
Html代碼 <a:form action="baz"><a:textfield label="Enter your name" name="person.name"/><a:submit value="Create person"/><a:submit name="redirect:www.google.com" value="Cancel"/></a:form>?
Redirect-action prefix:重定向action,下例中為定向到dashboard.action
Html代碼 <a:form action="baz"><a:textfield label="Enter your name" name="person.name"/><a:submit value="Create person"/><a:submit name="redirect-action:dashboard" value="Cancel"/></a:form>?
handleSpecialParameters的源代碼如下:
Java代碼 public void handleSpecialParameters(HttpServletRequest request, ActionMapping mapping) {Set<String> uniqueParameters = new HashSet<String>();Map parameterMap = request.getParameterMap();for (Iterator iterator = parameterMap.keySet().iterator(); iterator.hasNext();) {String key = (String) iterator.next();if (key.endsWith(".x") || key.endsWith(".y")) {// 去掉圖片按鈕的位置信息,具體情況我也不是很了解key = key.substring(0, key.length() - 2);}// 處理四種特殊的prefix:Method prefix,Action prefix,Redirect prefix,Redirect-action prefixif (!uniqueParameters.contains(key)) {ParameterAction parameterAction = (ParameterAction) prefixTrie.get(key);if (parameterAction != null) {// 當發現某種特殊的predix時parameterAction.execute(key, mapping);// 調用它的execute方法,在DefaultActionMapper的構造函數中定義uniqueParameters.add(key);// 下邊已經break了為什么還要把key加入到排重的Set里呢??break;}}}}?(6) 處理調用的不是execute方法的情況:
Struts框架也可以處理"name!method"形式的action調用,碰到這種情況,在此處將name和method分別解析出來然后賦給ActionMapping對象。
?
?
2.3、dispatcher.serviceAction(request, response, servletContext, mapping);方法分析
Java代碼 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException {//包裝了Http的四個作用域,extraContext 保存了所有的servlet 容器的作用域和struts2 包裝的容器作用域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 值棧存在,則用這個,否則創建一個新的,保存在extraContext 中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);//獲得action 的配置信息String namespace = mapping.getNamespace();String name = mapping.getName();String method = mapping.getMethod();Configuration config = configurationManager.getConfiguration();//創建一個ActionProxyActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, extraContext, true, false);//如果method 為空,則設為“excue”proxy.setMethod(method);//保存值棧供struts2 使用request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());// if the ActionMapping says to go straight to a result, do it!//如果result 不為空的話,進行調轉if (mapping.getResult() != null) {Result result = mapping.getResult();//注入的是ActionInvactionresult.execute(proxy.getInvocation());} else {proxy.execute();}// If there was a previous value stack then set it back onto the requestif (stack != null) {request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);}} catch (ConfigurationException e) {LOG.error("Could not find action or result", e);sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);} catch (Exception e) {throw new ServletException(e);} finally {UtilTimerStack.pop(timerKey);}}?
??? (1)createContextMap(request, response, mapping, context);方法
Java代碼 public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,ActionMapping mapping, ServletContext context) {// request map wrapping the http request objectsMap requestMap = new RequestMap(request);// parameters map wrapping the http paraneters.Map params = null;if (mapping != null) {params = mapping.getParams();}Map requestParams = new HashMap(request.getParameterMap());if (params != null) {params.putAll(requestParams);} else {params = requestParams;}// session map wrapping the http sessionMap session = new SessionMap(request);// application map wrapping the ServletContextMap application = new ApplicationMap(context);//對上面的http 作用域包裝的map 進行封裝Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);//把mapping 也放進map 里extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);return extraContext;}?? 由此可以看出struts2 對servlet 容器的作用域都進行包裝成相應的Map ,然后放在extraContext? 統一進行保存。
來看看extraContext? 這個map 里放的是全部servlet 容器作用域還有相應的struts2的包裝map,和 locale,從下面的源碼中可以看出。
Java代碼 public HashMap<String,Object> createContextMap(Map requestMap,Map parameterMap,Map sessionMap,Map applicationMap,HttpServletRequest request,HttpServletResponse response,ServletContext servletContext) {HashMap<String,Object> extraContext = new HashMap<String,Object>();extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));extraContext.put(ActionContext.SESSION, sessionMap);extraContext.put(ActionContext.APPLICATION, applicationMap);Locale locale;if (defaultLocale != null) {locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());} else {locale = request.getLocale();}extraContext.put(ActionContext.LOCALE, locale);//extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));extraContext.put(StrutsStatics.HTTP_REQUEST, request);extraContext.put(StrutsStatics.HTTP_RESPONSE, response);extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);// helpers to get access to request/session/application scopeextraContext.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;}?
(2)ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
??????????????????? namespace, name, extraContext, true, false);默認由DefaultActionProxyFactory類創建ActionProxy 。
?
? ? proxy.prepare(); 在這方法中創建ActionInvocation(默認為DefaultActionInvocation),主要由ActionInvocation來調度Action 的實際操作
Java代碼 public void prepare() throws Exception {String profileKey = "create DefaultActionProxy: ";try {UtilTimerStack.push(profileKey);config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);if (config == null && unknownHandler != null) {config = unknownHandler.handleUnknownAction(namespace, actionName);}if (config == null) {String message;if ((namespace != null) && (namespace.trim().length() > 0)) {message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_PACKAGE_ACTION_EXCEPTION, Locale.getDefault(), new String[]{namespace, actionName});} else {message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_ACTION_EXCEPTION, Locale.getDefault(), new String[]{actionName});}throw new ConfigurationException(message);}invocation = new DefaultActionInvocation(objectFactory, unknownHandler, this, extraContext, true, actionEventListener);//如果method 為空,則this.method = "execute";resolveMethod();} finally {UtilTimerStack.pop(profileKey);}}?
? ?在創建ActionInvocation 的時候有個主要的方法 init();
Java代碼 protected DefaultActionInvocation(final ObjectFactory objectFactory, final UnknownHandler handler, final ActionProxy proxy, final Map extraContext, final boolean pushAction, final ActionEventListener actionEventListener) throws Exception {UtilTimerStack.profile("create DefaultActionInvocation: ", new UtilTimerStack.ProfilingBlock<Object>() {public Object doProfiling() throws Exception {DefaultActionInvocation.this.proxy = proxy;DefaultActionInvocation.this.objectFactory = objectFactory;DefaultActionInvocation.this.extraContext = extraContext;DefaultActionInvocation.this.pushAction = pushAction;DefaultActionInvocation.this.unknownHandler = handler;DefaultActionInvocation.this.actionEventListener = actionEventListener;init();//這里return null;}});}??
? ?init();方法,該方法創建了Action 和ActionContext
Java代碼 private void init() throws Exception {Map contextMap = createContextMap();//創建ActioncreateAction(contextMap);if (pushAction) {//把Action 放進值棧stack.push(action);}//創建ActionContextinvocationContext = new ActionContext(contextMap);invocationContext.setName(proxy.getActionName());// get a new List so we don't get problems with the iterator if someone changes the listList interceptorList = new ArrayList(proxy.getConfig().getInterceptors());interceptors = interceptorList.iterator();}???
??? 創建Action,通過objectFactory 進行創建,而這個類在struts.properties中可以重寫這個屬性 。默認為SpringObjectFactory:struts.objectFactory=spring,在前面BeanSelectionProvider中通過配置文件為ObjectFactory設置實現類??
Java代碼 protected void createAction(Map contextMap) {// load actionString 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);}}public Object buildAction(String actionName, String namespace, ActionConfig config, Map extraContext) throws Exception {return buildBean(config.getClassName(), extraContext);}public Object buildBean(String className, Map extraContext) throws Exception {return buildBean(className, extraContext, true);}public Object buildBean(String className, Map extraContext, boolean injectInternal) throws Exception {Class clazz = getClassInstance(className);Object obj = buildBean(clazz, extraContext);if (injectInternal) {injectInternalBeans(obj);}return obj;} Java代碼 ?protected Object injectInternalBeans(Object obj) {if (obj != null && container != null) {container.inject(obj);}return obj;}??
??? proxy.execute();方法是struts2 中流程的重要方法。
?
Java代碼 public String execute() throws Exception {ActionContext nestedContext = ActionContext.getContext();ActionContext.setContext(invocation.getInvocationContext());String retCode = null;String profileKey = "execute: ";try {UtilTimerStack.push(profileKey);//這個是重點,主要的攔截器功能在這實現,執行返回跳轉的字符串retCode = invocation.invoke();} finally {if (cleanupContext) {ActionContext.setContext(nestedContext);}UtilTimerStack.pop(profileKey);}return retCode;}??
? ?invoke 方法調用了DefaultActionInvocation的invoke()去實現Action的調用
Java代碼 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)-1for (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)-2executeResult();}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的調用:
?
?? invokeActionOnly()內部是使用invokeAction()去實現Action的調用的,源代碼如下:
Java代碼 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。
?
于是,最終的調用順序應該是:
Interceptor1
Interceptor2
Interceptor3
Action
PreResultListener
Result
Interceptor3
Interceptor2
Interceptor1
?
總結
以上是生活随笔為你收集整理的struts2 处理请求流程分析(结合源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Struts2技术详解
- 下一篇: struts2.2.1.1 类型转换问题