SpringMVC設(shè)計理念與DispatcherServlet的示例分析

這篇文章主要介紹SpringMVC設(shè)計理念與DispatcherServlet的示例分析,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

目前創(chuàng)新互聯(lián)已為近1000家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬主機(jī)、網(wǎng)站改版維護(hù)、企業(yè)網(wǎng)站設(shè)計、平樂網(wǎng)站維護(hù)等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

SpringMVC簡介

SpringMVC作為Struts2之后異軍突起的一個表現(xiàn)層框架,正越來越流行,相信javaee的開發(fā)者們就算沒使用過SpringMVC,也應(yīng)該對其略有耳聞。我試圖通過對SpringMVC的設(shè)計思想和源碼實現(xiàn)的剖析,從抽象意義上的設(shè)計層面和實現(xiàn)意義上的代碼層面兩個方面,逐一揭開SpringMVC神秘的面紗,本文的代碼,都是基于Spring的 3.1.3RELEASE版本。

任何一個框架,都有自己特定的適用領(lǐng)域,框架的設(shè)計和實現(xiàn),必定是為了應(yīng)付該領(lǐng)域內(nèi)許多通用的,煩瑣的、基礎(chǔ)的工作而生。SpringMVC作為一個表現(xiàn)層框架,也必須直面Web開發(fā)領(lǐng)域中表現(xiàn)層中的幾大課題,并給出自己的回答:

  • URL到框架的映射。

  • http請求參數(shù)綁定

  • http響應(yīng)的生成和輸出

這三大課題,組成一個完整的web請求流程,每一個部分都具有非常廣闊的外延。SpringMVC框架對這些課題的回答又是什么呢?

學(xué)習(xí)一個框架,首要的是要先領(lǐng)會它的設(shè)計思想。從抽象、從全局上來審視這個框架。其中最具有參考價值的,就是這個框架所定義的核心接口。核心接口定義了框架的骨架,也在最抽象的意義上表達(dá)了框架的設(shè)計思想。

下面我以一個web請求流程為載體,依次介紹SpringMVC的核心接口和類。

用戶在瀏覽器中,輸入了http://www.xxxx.com/aaa/bbb.ccc的地址,回車后,瀏覽器發(fā)起一個http請求。請求到達(dá)你的服務(wù)器后,首先會被SpringMVC注冊在web.xml中的前端轉(zhuǎn)發(fā)器DispatcherServlet接收,DispatcherServlet是一個標(biāo)準(zhǔn)的Servlet,它的作用是接受和轉(zhuǎn)發(fā)web請求到內(nèi)部框架處理單元。

HandlerMapping接口

下面看一下第一個出現(xiàn)在你面前的核心接口,它是在org.springframework.web.servlet包中定義的HandlerMapping接口:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
public interface HandlerMapping {
	String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
	String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
	String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
	String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
	String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

為了閱讀方便,我去掉了源碼中的注釋,但是我強(qiáng)烈建議你一定要記得去閱讀它,這樣你才能從框架的設(shè)計者口中得到最準(zhǔn)確的關(guān)于這個類或者接口的設(shè)計說明。類中定義的幾個常量,我們先不去管它。關(guān)鍵在于這個接口中唯一的方法:

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

這個方法就算對于一個java初學(xué)者來說,也很容易理解:它只有一個類型為HttpServletRequest的參數(shù),throws Exception的聲明表示它不處理任何類型的異常,HandlerExecutionChain是它的返回類型。

DispatcherServlet接受請求并找到對應(yīng)Handler

回到DispatcherServlet的處理流程,當(dāng)DispatcherServlet接收到web請求后,由標(biāo)準(zhǔn)Servlet類處理方法doGet或者doPost,經(jīng)過幾次轉(zhuǎn)發(fā)后,最終注冊在DispatcherServlet類中的HandlerMapping實現(xiàn)類組成的一個List(有點拗口)會在一個循環(huán)中被遍歷。以該web請求的HttpServletRequest對象為參數(shù),依次調(diào)用其getHandler方法,第一個不為null的調(diào)用結(jié)果,將被返回。DispatcherServlet類中的這個遍歷方法不長,貼一下,讓大家有更直觀的了解。

/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
	 */
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

是的,第一步處理就這么簡單的完成了。一個web請求經(jīng)過處理后,會得到一個HandlerExecutionChain對象,這就是SpringMVC對URl映射給出的回答。需要留意的是,HandlerMapping接口的getHandler方法參數(shù)是HttpServletRequest,這意味著,HandlerMapping的實現(xiàn)類可以利用HttpServletRequest中的 所有信息來做出這個HandlerExecutionChain對象的生成”決策“。這包括,請求頭、url路徑、cookie、session、參數(shù)等等一切你從一個web請求中可以得到的任何東西(最常用的是url路徑)。

SpirngMVC的第一個擴(kuò)展點,就出現(xiàn)在這里。我們可以編寫任意的HandlerMapping實現(xiàn)類,依據(jù)任何策略來決定一個web請求到HandlerExecutionChain對象的生成??梢哉f,從第一個核心接口的聲明開始,SpringMVC就把自己的靈活性和野心暴露無疑:哥玩的就是”O(jiān)pen-Closed“。

HandlerExecutionChain這個類,就是我們下一個要了解的核心類。從名字可以直觀的看得出,這個對象是一個執(zhí)行鏈的封裝。熟悉Struts2的都知道,Action對象也是被層層攔截器包裝,這里可以做個類比,說明SpringMVC確實是吸收了Struts2的部分設(shè)計思想。

HandlerExecutionChain類的代碼不長,它定義在org.springframework.web.servlet包中,為了更直觀的理解,先上代碼。

package org.springframework.web.servlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.CollectionUtils;
public class HandlerExecutionChain {
	private final Object handler;
	private HandlerInterceptor[] interceptors;
	private List<HandlerInterceptor> interceptorList;
	public HandlerExecutionChain(Object handler) {
		this(handler, null);
	}
	public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {
		if (handler instanceof HandlerExecutionChain) {
			HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
			this.handler = originalChain.getHandler();
			this.interceptorList = new ArrayList<HandlerInterceptor>();
			CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
			CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
		}
		else {
			this.handler = handler;
			this.interceptors = interceptors;
		}
	}
	public Object getHandler() {
		return this.handler;
	}
	public void addInterceptor(HandlerInterceptor interceptor) {
		initInterceptorList();
		this.interceptorList.add(interceptor);
	}
	public void addInterceptors(HandlerInterceptor[] interceptors) {
		if (interceptors != null) {
			initInterceptorList();
			this.interceptorList.addAll(Arrays.asList(interceptors));
		}
	}
	private void initInterceptorList() {
		if (this.interceptorList == null) {
			this.interceptorList = new ArrayList<HandlerInterceptor>();
		}
		if (this.interceptors != null) {
			this.interceptorList.addAll(Arrays.asList(this.interceptors));
			this.interceptors = null;
		}
	}
	public HandlerInterceptor[] getInterceptors() {
		if (this.interceptors == null && this.interceptorList != null) {
			this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
		}
		return this.interceptors;
	}
	@Override
	public String toString() {
		if (this.handler == null) {
			return "HandlerExecutionChain with no handler";
		}
		StringBuilder sb = new StringBuilder();
		sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");
		if (!CollectionUtils.isEmpty(this.interceptorList)) {
			sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");
			if (this.interceptorList.size() > 1) {
				sb.append("s");
			}
		}
		return sb.toString();
	}
}

亂七八糟一大堆,相信你也沒全看完,也沒必要全看。其實只需要看兩行足矣。

private final Object handler;
	private HandlerInterceptor[] interceptors;

不出我們所料,一個實質(zhì)執(zhí)行對象,還有一堆攔截器。這不就是Struts2中的實現(xiàn)么,SpringMVC沒有避嫌,還是采用了這種封裝。得到HandlerExecutionChain這個執(zhí)行鏈(execution chain)之后,下一步的處理將圍繞其展開。

HandlerInterceptor接口

HandlerInterceptor也是SpringMVC的核心接口,定義如下:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerInterceptor {
	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
	    throws Exception;
	void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception;
	void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception;
}

至此,HandlerExecutionChain整個執(zhí)行脈絡(luò)也就清楚了:在真正調(diào)用其handler對象前,HandlerInterceptor接口實現(xiàn)類組成的數(shù)組將會被遍歷,其preHandle方法會被依次調(diào)用,然后真正的handler對象將被調(diào)用。

handler對象被調(diào)用后,就生成了需要的響應(yīng)數(shù)據(jù),在將處理結(jié)果寫到HttpServletResponse對象之前(SpringMVC稱為渲染視圖),其postHandle方法會被依次調(diào)用。視圖渲染完成后,最后afterCompletion方法會被依次調(diào)用,整個web請求的處理過程就結(jié)束了。

在一個處理對象執(zhí)行之前,之后利用攔截器做文章,這已經(jīng)成為一種經(jīng)典的框架設(shè)計套路。Struts2中的攔截器會做諸如參數(shù)綁定這類復(fù)雜的工作,那么SpringMVC的攔截器具體做些什么呢?我們暫且不關(guān)心,雖然這是很重要的細(xì)節(jié),但細(xì)節(jié)畢竟是細(xì)節(jié),我們先來理解更重要的東西。

HandlerInterceptor,是SpringMVC的第二個擴(kuò)展點的暴露,通過自定義攔截器,我們可以在一個請求被真正處理之前、請求被處理但還沒輸出到響應(yīng)中、請求已經(jīng)被輸出到響應(yīng)中之后這三個時間點去做任何我們想要做的事情。Struts2框架的成功,就是源于這種攔截器的設(shè)計,SpringMVC吸收了這種設(shè)計思想,并推陳出新,更合理的劃分了三個不同的時間點,從而給web請求處理這個流程,提供了更大的擴(kuò)展性。

這個HandlerExecutionChain類中以O(shè)bject引用所聲明的handler對象,到底是個什么東東?它是怎么被調(diào)用的?

HandlerAdapter

回答這些問題之前,先看SpringMVC中的又一個核心接口,HandlerAdapter:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerAdapter {
	boolean supports(Object handler); 
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	long getLastModified(HttpServletRequest request, Object handler);
}

在DispatcherServlet中,除了HandlerMapping實現(xiàn)類的列表,同樣也注冊了一個HandlerAdapter實現(xiàn)類組成的列表,有代碼為證。

/** List of HandlerMappings used by this servlet */
	private List<HandlerMapping> handlerMappings;
	/** List of HandlerAdapters used by this servlet */
	private List<HandlerAdapter> handlerAdapters;

接下來,我們再以DispatcherServlet類中另外一段代碼來回答上述的問題:

/**
	 * Return the HandlerAdapter for this handler object.
	 * @param handler the handler object to find an adapter for
	 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
	 */
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: Does your handler implement a supported interface like Controller?");
	}

請求流程總結(jié)

這段代碼已經(jīng)很明顯了,HandlerExecutionChain中的handler對象會被作為參數(shù)傳遞進(jìn)去,在DispatcherServlet類中注冊的HandlerAdapter實現(xiàn)類列表會被遍歷,然后返回第一個supports方法返回true的HandlerAdapter對象,用這個HandlerAdapter實現(xiàn)類中的handle方法處理handler對象,并返回ModelAndView這個包含了視圖和數(shù)據(jù)的對象。HandlerAdapter就是SpringMVC提供的第三個擴(kuò)展點,你可以提供自己的實現(xiàn)類來處理handler對象。

ModelAndView對象的代碼就不貼了,它是SpringMVC中對視圖和數(shù)據(jù)的一個聚合類。其中的視圖,就是由SpringMVC的最后一個核心接口View所抽象:

package org.springframework.web.servlet;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface View {
	String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
	String PATH_VARIABLES = View.class.getName() + ".pathVariables";
	String getContentType();
	void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

所有的數(shù)據(jù),最后會作為一個Map對象傳遞到View實現(xiàn)類中的render方法,調(diào)用這個render方法,就完成了視圖到響應(yīng)的渲染。這個View實現(xiàn)類,就是來自HandlerAdapter中的handle方法的返回結(jié)果。當(dāng)然從ModelAndView到真正的View實現(xiàn)類有一個解析的過程,ModelAndView中可以有真正的視圖對象,也可以只是有一個視圖的名字,SpringMVC會負(fù)責(zé)將視圖名稱解析為真正的視圖對象。

至此,我們了解了一個典型的完整的web請求在SpringMVC中的處理過程和其中涉及到的核心類和接口。

在一個典型的SpringMVC調(diào)用中,HandlerExecutionChain中封裝handler對象就是用@Controller注解標(biāo)識的類的一個實例,根據(jù)類級別和方法級別的@RequestMapping注解,由默認(rèn)注冊的DefaultAnnotationHandlerMapping(3.1.3中更新為RequestMappingHandlerMapping類,但是為了向后兼容,DefaultAnnotationHandlerMapping也可以使用)生成HandlerExecutionChain對象,再由AnnotationMethodHandlerAdapter(3.1.3中更新為RequestMappingHandlerAdapter類,但是為了向后兼容,AnnotationMethodHandlerAdapter也可以使用)來執(zhí)行這個HandlerExecutionChain對象,生成最終的ModelAndView對象后,再由具體的View對象的render方法渲染視圖。

可以看到,作為一個表現(xiàn)層框架,SpringMVC沒有像Struts2那樣激進(jìn),并沒有采用和Web容器完全解耦的設(shè)計思想,而是以原生的Servlet框架對象為依托,通過合理的抽象,制定了嚴(yán)謹(jǐn)?shù)牡奶幚砹鞒?。這樣做的結(jié)果是,執(zhí)行效率比Struts2要高,靈活性也上升了一個層次。

以上是“SpringMVC設(shè)計理念與DispatcherServlet的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

網(wǎng)站欄目:SpringMVC設(shè)計理念與DispatcherServlet的示例分析
URL鏈接:http://muchs.cn/article36/pisosg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)、品牌網(wǎng)站建設(shè)響應(yīng)式網(wǎng)站、自適應(yīng)網(wǎng)站企業(yè)網(wǎng)站制作、外貿(mào)建站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計