上一篇
Spring MVC 核心類: HandlerMapping(一)
2. 詳細(xì)說(shuō)明
2.1 AbstractHandlerMapping
HandlerMapping 實(shí)現(xiàn)的基礎(chǔ)類。這個(gè)基礎(chǔ)類不支持暴露PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE 屬性。這個(gè)屬性的暴露取決于具體的子類實(shí)現(xiàn),例如典型的通過(guò)URL映射來(lái)實(shí)現(xiàn)。
這個(gè)超類定義了默認(rèn)handler、排序、攔截器的基本實(shí)現(xiàn)。
2.1.1 默認(rèn)handler設(shè)置:
默認(rèn)為null。主要用于當(dāng)無(wú)法匹配到handler時(shí),返回這個(gè)具體指定的handler。
/**
* Set the default handler for this handler mapping.
* This handler will be returned if no specific mapping was found.
* <p>Default is {@code null}, indicating no default handler.
*/
public void setDefaultHandler(@Nullable Object defaultHandler) {
this.defaultHandler = defaultHandler;
}
2.1.2 順序
默認(rèn)為最低優(yōu)先級(jí),主要通過(guò)實(shí)現(xiàn) Spring 框架中的 Ordered 接口來(lái)支撐。
private int order = Ordered.LOWEST_PRECEDENCE;
/**
* Specify the order value for this HandlerMapping bean.
* <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered.
* @see org.springframework.core.Ordered#getOrder()
*/
public void setOrder(int order) {
this.order = order;
}
2.1.3 URL 處理
通過(guò) UrlPathHelper ,把URL的處理能力轉(zhuǎn)交出去,從而實(shí)現(xiàn)各種URL處理的能力,例如實(shí)現(xiàn)ANT模式的URL處理方式。同時(shí)也提供了便利的設(shè)置 UrlPathHelper 屬性的方法,方便直接配置:
- 可以設(shè)置一直使用全路徑匹配;
- 可以設(shè)置URL的decode操作;
- 可以設(shè)置是否移除URL中分號(hào)的內(nèi)容
通過(guò) UrlPathHelper, Spring 框架可以提供靈活的URL處理方式,后續(xù) HandlerMapping 子類實(shí)現(xiàn)中會(huì)使用到具體的實(shí)例來(lái)完成對(duì)應(yīng)的功能。
2.1.4 攔截器(interceptor)
1.首先可以設(shè)置攔截器
設(shè)置匹配這個(gè)Handler映射的所有攔截器。目前支持的攔截器類型有HandlerInterceptor、WebRequestInterceptor、MappedInterceptor 三種。通過(guò)路徑匹配找到對(duì)應(yīng)攔截器處理。
/**
* Set the interceptors to apply for all handlers mapped by this handler mapping.
* <p>Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor.
* Mapped interceptors apply only to request URLs that match its path patterns.
* Mapped interceptor beans are also detected by type during initialization.
* @param interceptors array of handler interceptors
* @see #adaptInterceptor
* @see org.springframework.web.servlet.HandlerInterceptor
* @see org.springframework.web.context.request.WebRequestInterceptor
*/
public void setInterceptors(Object... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
}
2.初始化過(guò)程
首先 Spring 框架提供了在初始化或者是正式使用前的擴(kuò)展點(diǎn)。支持用戶自定義增加或者其他想要的操作來(lái)處理將要被配置的攔截器列表。
再次是在Spring Bean 容器中查找 MappedInterceptor 所有實(shí)例,包含父 Spring Bean 容器中的所有 MappedInterceptor 實(shí)例。同時(shí)把查找到的MappedInterceptor添加到adaptedInterceptors列表中。當(dāng)然這是默認(rèn)實(shí)現(xiàn),如果你想改變這種策略,Spring 框架是支持通過(guò)重寫 detectMappedInterceptors 方法來(lái)擴(kuò)展。
最后把把 interceptors 列表中的攔截器添加到 adaptedInterceptors中。放入代理的攔截器列表前會(huì)通過(guò) MappedInterceptor 具體實(shí)現(xiàn)類型進(jìn)行包裝:如果直接實(shí)現(xiàn)HandlerInterceptor不包裝;如果實(shí)現(xiàn)WebRequestInterceptor就使用 WebRequestHandlerInterceptorAdapter包裝。在默認(rèn)實(shí)現(xiàn)中僅僅支持這兩種類型的處理,如果需要可以自己擴(kuò)展。
通過(guò)上面的處理,adaptedInterceptors裝載來(lái)所有的攔截器。
/**
* Initializes the interceptors.
* @see #extendInterceptors(java.util.List)
* @see #initInterceptors()
*/
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
2.1.5 獲取Handler(getHandler)
根據(jù)Request,查找對(duì)應(yīng)的Handler。當(dāng)沒(méi)有匹配的Handler時(shí),使用指定的默認(rèn)Handler。
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// 如果是使用的BeanName,從spring容器中獲取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
...
return executionChain;
}
實(shí)現(xiàn)中有兩次擴(kuò)展,可以供子類使用:
- getHandlerInternal :
- 真正的執(zhí)行通過(guò)指定的Request查找對(duì)應(yīng)的Handler,當(dāng)無(wú)法匹配到對(duì)應(yīng)的Handler 時(shí),返回
null。通過(guò)上面的 getHandler 方法的調(diào)用,我們可以看出,當(dāng)此方法返回null時(shí),會(huì)觸發(fā)默認(rèn) Handler 賦值。 - 這個(gè)方法你可以返回一個(gè)已經(jīng)包裝好的
HandlerExecutionChain。因?yàn)?HandlerExecutionChain 中已經(jīng)存在了相關(guān)攔截器的配置,所以這些攔截器會(huì)動(dòng)態(tài)和HandlerMapping中配置的攔截器進(jìn)行合并。也就是說(shuō)靜態(tài)的攔截器會(huì)合并到已經(jīng)提供的 HandlerExecutionChain 中。
- 真正的執(zhí)行通過(guò)指定的Request查找對(duì)應(yīng)的Handler,當(dāng)無(wú)法匹配到對(duì)應(yīng)的Handler 時(shí),返回
- getHandlerExecutionChain
- getHandler 方法在調(diào)用getHandlerInternal后獲取到具體的handler后調(diào)用該方法。主要用于構(gòu)建一個(gè) Handler 處理器的處理鏈路。在構(gòu)建這個(gè)處理鏈時(shí),會(huì)選擇合適的攔截器。
- 構(gòu)建一個(gè)默認(rèn)標(biāo)準(zhǔn)的
HandlerExecutionChain包含指定的Handler、通用映射對(duì)應(yīng)的攔截器、以及和當(dāng)前請(qǐng)求URL匹配的MappedInterceptor。攔截器的順序依賴注冊(cè)順序的先后。如果需求,子類可以重寫這個(gè)順序來(lái)擴(kuò)展/重新構(gòu)建這個(gè)攔截器列表。 - 這個(gè)方法支持用原始的Handler創(chuàng)建新的HandlerExecutionChain和傳入的HandlerExecutionChain兩種方式。
如果僅僅是簡(jiǎn)單的在子類中增加一個(gè)攔截器,可以考慮調(diào)用super.getHandlerExecutionChain(handler, request),同時(shí)調(diào)用 HandlerExecutionChain 中的addInterceptor方法。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
2.2 AbstractUrlHandlerMapping
用URL映射方式的基礎(chǔ)HandlerMapping實(shí)現(xiàn)類。本類提供了通過(guò)URL或者配置的具體的URL查找方式的HandlerMapping實(shí)現(xiàn)的基礎(chǔ)設(shè)施,對(duì)于后者,可以通過(guò)“alwaysUseFullPath”屬性來(lái)了解更多。
支持直接匹配,例如配置 “/test” 來(lái)映射 “/test”;或者使用ANT形式來(lái)匹配,例如配置“/t”來(lái)映射“/test”和“/team”,使用“/test/”來(lái)匹配所有“/test”的直接目錄下的URL,使用“/test/**”來(lái)匹配“/test”下面所有的URL包含子目錄下的鏈接。更多配置詳情,可以參考 AntPathMatcher 。
匹配過(guò)程中會(huì)查詢所有匹配路徑,同時(shí)找到最長(zhǎng)匹配作為最精確的匹配。
同時(shí)此基礎(chǔ)類定義了handlerMap,這個(gè)Map定義了URL映射關(guān)系。本基礎(chǔ)類提供了Map注冊(cè)的模版方法(registerHandler),供其子類調(diào)用。
2.2.1 設(shè)置根目錄對(duì)應(yīng)的Handler,即為“/”的handler。默認(rèn)情況為null
/**
* Set the root handler for this handler mapping, that is,
* the handler to be registered for the root path ("/").
* <p>Default is {@code null}, indicating no root handler.
*/
public void setRootHandler(Object rootHandler) {
this.rootHandler = rootHandler;
}
2.2.2 覆蓋getHandlerInternal 方法
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request);
// 當(dāng)通過(guò)URL路徑無(wú)法匹配時(shí),優(yōu)先處理根Handler,再使用默認(rèn)Handler。
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
…
return handler;
}
2.2.3 查到Handler (lookupHandler)
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 1. 如果可以通過(guò)URL直接映射,使用這個(gè)映射對(duì)應(yīng)的Handler
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 2. 通過(guò)匹配進(jìn)行映射
List<String> matchingPatterns = new ArrayList<String>();
// 2.1 找到所有的可能的映射對(duì)應(yīng)的Handler
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
// 2.2 決策最優(yōu)映射
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
// 通過(guò)路徑比較器對(duì)所有匹配的映射進(jìn)行排序,最長(zhǎng)映射放在第一位
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
// 返回第一個(gè)映射,即為最優(yōu)映射
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
// 通過(guò)最優(yōu)的匹配路徑來(lái)找對(duì)應(yīng)的Handler,如果沒(méi)有找到對(duì)應(yīng)的Handler,報(bào)錯(cuò)
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
2.2.4 模版方法:registerHandler
通過(guò)給定的URL路徑,注冊(cè)指定的Handler。
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
...
Object resolvedHandler = handler;
...
// 從HandlerMap中獲取URL路徑對(duì)應(yīng)的Handler
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
// 如果出現(xiàn)一個(gè)URL路徑兩個(gè)匹配的Handler的情況報(bào)錯(cuò)
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(...);
}
}
else {
// 如果是根目錄的情況下,設(shè)置根目錄處理器
if (urlPath.equals("/")) {
...
setRootHandler(resolvedHandler);
}
// 如果是根目錄匹配的情況下,設(shè)置根目錄處理器
else if (urlPath.equals("/*")) {
...
setDefaultHandler(resolvedHandler);
}
// 其他情況下,設(shè)置URL路徑和處理器的映射
else {
this.handlerMap.put(urlPath, resolvedHandler);
...
}
}
}
2.3 AbstractUrlHandlerMapping 子類
2.3.1 BeanNameUrlHandlerMapping
該類是 AbstractDetectingUrlHandlerMapping 的子類。在 AbstractDetectingUrlHandlerMapping 中定義了檢測(cè)所有Bean的基礎(chǔ)方法。其子類通過(guò)擴(kuò)展 determineUrlsForHandler 方法進(jìn)行判斷是否需要進(jìn)行URL注冊(cè)。
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
...
}
BeanNameUrlHandlerMapping 擴(kuò)展了 determineUrlsForHandler,定義通過(guò)Bean名稱來(lái)定義URL,通過(guò)配置Bean名稱的別名(以“/”開(kāi)頭)來(lái)確定對(duì)應(yīng)的URL:
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
2.3.2 SimpleUrlHandlerMapping
這個(gè)類是通過(guò)配置URL到對(duì)應(yīng)的處理器的Bean名稱的方式的HandlerMapping。支持URL到bean實(shí)例和URL到bean名稱兩種方式。使用Bean名稱映射時(shí),僅僅支持該Bean為單例模式,否則無(wú)法通過(guò)Bean名稱初始化。
配置URL到處理器Handler有兩種方式:
1.通過(guò)屬性文件配置,屬性文件的的樣子如下:
/welcome.html=ticketController
/show.html=ticketController
2.直接通過(guò)XML配置 urlMap
/**
* Set a Map with URL paths as keys and handler beans (or handler bean names)
* as values. Convenient for population with bean references.
* <p>Supports direct URL matches and Ant-style pattern matches. For syntax
* details, see the {@link org.springframework.util.AntPathMatcher} javadoc.
* @param urlMap map with URLs as keys and beans as values
* @see #setMappings
*/
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
同時(shí)本類定義了處理URL集合的方法
/**
* 觸發(fā)解析URL配置
*/
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.trace("No patterns in " + formatMappingName());
}
else {
urlMap.forEach((url, handler) -> {
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
});
...
}
}
綜上,這個(gè)類處理的核心是配置具體的URL到Controller的映射關(guān)系。當(dāng)然URL可以遵循 ANT 模式。
2.3.3 WelcomePageHandlerMapping
首先在構(gòu)建時(shí)定義默認(rèn)的Controller和根路徑處理Handler:
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
ApplicationContext applicationContext, Optional<Resource> welcomePage,
String staticPathPattern) {
if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage.get());
setRootViewName("forward:index.html");
}
else if (welcomeTemplateExists(templateAvailabilityProviders,
applicationContext)) {
logger.info("Adding welcome page template: index");
setRootViewName("index");
}
}
private void setRootViewName(String viewName) {
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName(viewName);
setRootHandler(controller);
setOrder(2);
}
再次覆蓋 getHandlerInternal 方法,使用超類中通過(guò)URL匹配不到就判斷是否為根路徑同時(shí)使用根路徑對(duì)應(yīng)的處理Handler的邏輯,達(dá)到使用通用的歡迎頁(yè)的Controller的目的。
@Override
public Object getHandlerInternal(HttpServletRequest request) throws Exception {
for (MediaType mediaType : getAcceptedMediaTypes(request)) {
if (mediaType.includes(MediaType.TEXT_HTML)) {
return super.getHandlerInternal(request);
}
}
return null;
}
綜上,這個(gè)類處理的核心是配置Index對(duì)應(yīng)的Controller為默認(rèn)的根處理Handler。
下一篇:方法級(jí)別的映射分析
Spring MVC 核心類: HandlerMapping(三)