日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

struts2处理请求流程详解

發布時間:2025/4/14 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 struts2处理请求流程详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

struts2大概分為兩塊:一是struts2系統初始化,二是struts2處理請求,對請求作出響應。

下面就說說個人對struts2對請求處理流程的理解:

下面是StrutsPrepareAndExecuteFilter過濾器的doFilter方法中的主要代碼:

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);}}

?

在系統初始化的時候StrutsPrepareAndExecuteFilter的init方法被執行,實例化出了PrepareOperations和ExecuteOperations兩個對象,第一個對象是對真正響應請求之前所作的一些準備操作封裝,ExecuteOperations是對響應請求所作的封裝,但其實這兩個對象最終調用的都是核心分發器Dispatcher對象的方法。


prepare.setEncodingAndLocale(request, response);這一句處理請求編碼與響應Locale,其內部調用的就是Dipatcher的prepare方法,下面是源碼:

?

/*** Sets the request encoding and locale on the response*/public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {dispatcher.prepare(request, response);}

其邏輯是如果在在struts2的配置文件中指寫了i18n編碼則使用配置文件中的編碼,否則不會調用request.setCharacterEncoding()方法。至于響應Locale的取值與encoding的原理是一樣的,但具體的邏輯源碼有點多,但不難,這里就不作解釋了,大家應該都看得懂。

?


prepare.createActionContext(request, response);

ActionContext oldContext = ActionContext.getContext();if (oldContext != null) {// detected existing context, so we are probably in a forward //因為項目一般都不會是分布式應用,也就不會執行這里ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));} else { //這里是會執行的代碼ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));ctx = new ActionContext(stack.getContext());}request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);ActionContext.setContext(ctx);return ctx;

ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();從struts2容器中獲取ValueStackFactory并創建出ValueStack

?

stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));把ActionContext中的數據復制一份到ValueStack中,所以從ActionContext與ValueStack都能拿到我們想要的所有數據。

dispatcher.createContextMap

?

// request map wrapping the http request objectsMap requestMap = new RequestMap(request);// parameters map wrapping the http parameters. ActionMapping parameters are now handled and applied separatelyMap params = new HashMap(request.getParameterMap());// session map wrapping the http sessionMap session = new SessionMap(request);// application map wrapping the ServletContextMap application = new ApplicationMap(context);Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);if (mapping != null) {extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);}return extraContext;


該方法中把Servlet原生的Request,Parameters,Session,ServletContext都轉換成了Map,然后將轉換后的Map與原生的Servlet對象都傳進了Dispatcher另一個重載的createContextMap方法,下面是其源碼:

?

?

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;}


該方法主要就是把轉換后的Map與Servlet原生對象外加一個AttributeMap都存進了一個大Map中,然后返回。

?

這樣就即能獲取轉換后的Map,又可以獲取Servlet原生對象。


現在回到prepare.createActionContext方法中

ActionContext.setContext(ctx);把創建的ActionContext對象放進ThreadLocal<T>中,綁定到當前線程以在其它地方方便獲取ActionContext對象。


再加到核心過濾器中,prepare.assignDispatcherToThread();從該方法的名稱就知道是把Dispatcher對象綁定到當前線程,也就是放進了一個ThreadLocal<T>對象中,這樣做的目的是為了解決多線程并發訪問的問題,因為Dispathcer對象只創建了一個,創建代碼就在StrutsPrepareAndExecuteFilter的init方法當中,而init方法只會執行一次,當然Dispatcher對象也就只有一個了,而Web應用天生就是一個多線程的環境,所以把Dispatcher放進ThreadLocal<T>中成為了最佳選擇。這里與上面ActionContext對象放進ThreadLocal<T>中的原因是不一樣的,因為每當一個請求到來系統都會為其創建一個ActionContext對象,這個ActionContext是該請求獨享的,并不存在多線程的問題,所以把該對象放進ThreadLocal<T>中是為了獲取方便。


if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
}該判斷是因為struts2支持不包含的URL模式匹配,一個請求雖然進入了struts2過濾器,但如果該請求的URL在不包含之列的話sturts2只是單純地調用chain.doFilter方法放行。其它情況就會進入else部分,調用Action處理請求。

request = prepare.wrapRequest(request);對HttpServletRequest進行包裝,參看其源碼就可以知道如果是文件上傳把HttpServletRequest包裝成了一個MultiPartRequestWrapper對象,該對象專門處理文件上傳請求。如果不是文件上傳進把HttpServletRequest包裝成了StrutsRequestWrapper,StrutsRequestWrapper類覆蓋了其父類的getAttribute方法,對該方法的行為作了一點修改,其父類即javax.servlet.http.HttpServletRequestWrapper中的getAttribute方法只是從request對象查找指定屬性,而StrutsRequestWrapper的getAttribute方法是先在request對象中進行查找,如果沒有找到還會去ValueStack中進行查找,下面的源碼即是證明:

?

// If not found, then try the ValueStackctx.put("__requestWrapper.getAttribute", Boolean.TRUE);ValueStack stack = ctx.getValueStack();if (stack != null) {attribute = stack.findValue(s);}

這就是為什么在JSP頁面中用EL表達式也能訪問到ValueStack中值的屬性的原因。

?


ActionMapping mapping = prepare.findActionMapping(request, response, true);這句就沒什么說了,就是得到Action映射信息。


如果請求的靜態頁面就會執行execute.executeStaticResourceRequest(request, response);在方法內部會判斷有無能力對該請求進行處理,如果有則處理沒有則調用chain.doFilter放行,實現基本都一樣就是原樣返回給客戶端了。


真正會執行Action的是這一句:execute.executeAction(request, response, mapping);該方法調用的是Dispatcher的serviceAction方法,我們進入該方法看看,下面是該方法中的重要代碼:

?

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 requestif (!nullStack) {request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);}


先獲取Configuration對象,通過Configuration得到容器對象,再從容器中獲取ActionProxyFactory,ActionProxy工廠類,然后創建ActionProxy,大家都知道struts2內部包裝的是webwork框架,而ActionProxy正是sturts與webwork的分界線,ActionProxy是進行webwork框架的門戶。

?


struts2默認使用的是DefaultActionProxyFactory創建ActionProxy對象的,下面是其createActionFactor方法:

?

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);} public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);container.inject(proxy);proxy.prepare();return proxy;}

這是兩個重載的方法,第一個方法調用了第二個方法,在第一個方法中創建了ActionInvocation對象,并對其依賴的對象使用容器進行注入,緊接著就創建了ActionProxy對象,也對其依賴對象進行了注入,如果ObjectFctory,Configuration對象等。然后高用proxy.prepare()方法,其中有一個resolveMethod();的方法,該方法很簡單,就是如果在配置Action的時候沒有指定method屬性的時候會把method屬性值賦為execute,這就是為什么execute是Action默認的執行方法的原因。還有一個很重要的方法叫invocation.init(this);即調用ActionInvocation的init方法,把ActionProxy自己傳了進行,當然ActionInvocation中會對ActionProxy進行緩存。

?

struts2對ActionInvocation的默認實現是DefaultActionInvocation類,進放該類的init方法,下面是該方法中重要的代碼:

?

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 listList<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());interceptors = interceptorList.iterator();

第一句即創建Action,如果大家對struts2的對象創建有所了解的話就知道,Action,Result,Intercepter的創建都是由ObjectFactory的buildBean方法來創建的,其內部就是調用Class.newInstance();創建的,所以Action一定要有一個無參構造方法。

?

注意這句:stack.push(action);這里把創建的Action壓進了ValuesStack中,這就是為什么默認Action在棧頂的原因。下面就是獲取該Action配置的所有攔截器并進行緩存在ActionInvocation中,Action也是如此,因為Action與Interceptor的執行調度就是由ActionInvocation實現的。


現在回到Dispatcher的serviceAction方法中,創建出來ActionProxy對象后的下一句代碼把ValueStack對象放進了request中,這也就意味著我們通過HttpServletRequest對象也是可獲取,只要知道key就行了。


proxy.execute();這一句執行ActionProxy的execute方法,struts2中ActionProxy的默認實現是StrutsActionProxy,下面進行該類的execute方法,源碼如下:

?

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方法,執行攔截器與Action,下面是invoke方法的源碼,因該方法中附屬代碼較多,這里只撿出了重要代碼:

?

?

if (interceptors.hasNext()) {final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();String interceptorMsg = "interceptor: " + interceptor.getName();UtilTimerStack.push(interceptorMsg);try {resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);}finally {UtilTimerStack.pop(interceptorMsg);}} else {resultCode = invokeActionOnly();} //這里省略了一些代碼... // now execute the result, if we're supposed toif (proxy.getExecuteResult()) {executeResult();}

顯示這里就是在執行攔截器棧中的所有攔截器,當攔截器執行完后就會根據配置執行Action中相應的方法,執行完后得到了一個resultCode字符串,系統就是根據這個字符串去查找相應的Result。

?

緊湊著就是執行executeResult方法,該方法中就是根據Result配置創建出相應的Result對象,然后執行Result的execute方法,例如用得最多的ServletDispatcherResult,其execute方法主要就是調用dispatcher.forward(request, response)方法,返回一個頁面給Tomcat進行解析,然后將解析后的內容呈現給客戶端瀏覽器。

至此,struts2的整個執行流程基本上就講完了,如果有錯誤之處,盡請指正。


下面上傳的是個人為struts2執行流程畫的一個時序圖,有興趣的可以看看,因圖片太大所以要放大了才看得清,希望有所幫助:



?

轉載于:https://www.cnblogs.com/pangblog/p/3357891.html

總結

以上是生活随笔為你收集整理的struts2处理请求流程详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。