Spring之SpringMVC的MethodNameResolver(源碼)分析

前言

  在介紹SpringMVC  的Controller的具體實現中,咱們講到了MultiActionController。在獲取處理請求對於的方法的時候咱們用到了下面的代碼,來自於MultiActionController的handleRequestInternal的方法:html

	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		try {
			String methodName = this.methodNameResolver.getHandlerMethodName(request);
			return invokeNamedMethod(methodName, request, response);
		}
		catch (NoSuchRequestHandlingMethodException ex) {
			return handleNoSuchRequestHandlingMethod(ex, request, response);
		}
	}

  MultiActionController經過methodNameResolver的getHandlerMethodName()方法獲取處理該請求的具體方法名字。來看下methodNameResolver具體實現。java

1.MethodNameResolver介紹

MethodNameResolver是個接口,來看看定義web

public interface MethodNameResolver {

	String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException;

}

  這個接口用於MultiActionController方法中獲取具體的參數,它是經過策略模式來實現的。它能夠經過具體的請求映射處處理的方法名上,他們的改變也不會影響到其餘應用程序。看看下面其餘的具體實現。MethodNameResolver的實現類包括ParameterMethodNameResolver、AbstractUrlMethodNameResolver、InternalPathMethodNameResolver、PropertiesMethodNameResolver。spring

2.ParameterMethodNameResolver

ParameterMethodNameResolver實現了MethodNameResolver接口,它支持經過參數值來映射到方法名上。經過註釋知道能夠經過下面的過程來獲取具體的方法:設計模式

  1.根據請求的參數名解析功能方法名;mvc

  2.根據請求參數名的值解析功能方法名,默認的參數名是 action,即請求的參數中含有「action=query」 ,則功能處理方法名爲 query;app

  3.邏輯功能方法名到真實功能方法名映射;this

  4.默認的方法名,當以上策略失敗時默認調用的方法名。url

來具體獲取處理器方法名過程:spa

{
		String methodName = null;

		// 檢查參數名是否存在
		if (this.methodParamNames != null) {
			for (String candidate : this.methodParamNames) {
				if (WebUtils.hasSubmitParameter(request, candidate)) {
					methodName = candidate;
					if (logger.isDebugEnabled()) {
						logger.debug("Determined handler method '" + methodName +
								"' based on existence of explicit request parameter of same name");
					}
					break;
				}
			}
		}

		// 檢查參數名的值是否存在
		if (methodName == null && this.paramName != null) {
			methodName = request.getParameter(this.paramName);
			if (methodName != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Determined handler method '" + methodName +
							"' based on value of request parameter '" + this.paramName + "'");
				}
			}
		}

		if (methodName != null && this.logicalMappings != null) {
			// Resolve logical name into real method name, if appropriate.
			String originalName = methodName;
			methodName = this.logicalMappings.getProperty(methodName, methodName);
			if (logger.isDebugEnabled()) {
				logger.debug("Resolved method name '" + originalName + "' to handler method '" + methodName + "'");
			}
		}

		if (methodName != null && !StringUtils.hasText(methodName)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Method name '" + methodName + "' is empty: treating it as no method name found");
			}
			methodName = null;
		}

		if (methodName == null) {
			if (this.defaultMethodName != null) {
				// No specific method resolved: use default method.
				methodName = this.defaultMethodName;
				if (logger.isDebugEnabled()) {
					logger.debug("Falling back to default handler method '" + this.defaultMethodName + "'");
				}
			}
			else {
				// If resolution failed completely, throw an exception.
				throw new NoSuchRequestHandlingMethodException(request);
			}
		}

		return methodName;
	}

  三、PropertiesMethodNameResolver

  PropertiesMethodNameResolver和InternalPathMethodNameResolver繼承了抽象類AbstractUrlMethodNameResolver。而AbstractUrlMethodNameResolver提供了方法名的獲取的方法。

public final String getHandlerMethodName(HttpServletRequest request)
			throws NoSuchRequestHandlingMethodException {

		String urlPath = this.urlPathHelper.getLookupPathForRequest(request);
		String name = getHandlerMethodNameForUrlPath(urlPath);
		if (name == null) {
			throw new NoSuchRequestHandlingMethodException(urlPath, request.getMethod(), request.getParameterMap());
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Returning handler method name '" + name + "' for lookup path: " + urlPath);
		}
		return name;
	}

  由上面的代碼可知,子類的差別在於 String name = getHandlerMethodNameForUrlPath(urlPath);這段代碼getHandlerMethodNameForUrlPath是抽象方法,具體的實現見子類。首先看下PropertiesMethodNameResolver的getHandlerMethodNameForUrlPath的實現:

PropertiesMethodNameResolver提供自定義的從請求 URL 解析功能方法的方法名,使用一組用戶自定義的模式到功能方法名的映射,映射使用 Properties 對象存放,

	protected String getHandlerMethodNameForUrlPath(String urlPath) {
		String methodName = this.mappings.getProperty(urlPath);
		if (methodName != null) {
			return methodName;
		}
		Enumeration propNames = this.mappings.propertyNames();
		while (propNames.hasMoreElements()) {
			String registeredPath = (String) propNames.nextElement();
			if (this.pathMatcher.match(registeredPath, urlPath)) {
				return (String) this.mappings.get(registeredPath);
			}
		}
		return null;
	}

  用法以下:

<bean id="propertiesMethodNameResolver"
	class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
	<property name="mappings">
		<props>
			<prop key="/create">create</prop>
			<prop key="/update">update</prop>
			<prop key="/delete">delete</prop>
			<prop key="/list">list</prop>
			<!-- 默認的行爲 -->
			<prop key="/**">list</prop>
		</props>
	</property>
</bean>

  4.InternalPathMethodNameResolver

InternalPathMethodNameResolver是MethodNameResolver的默認實現,在MultiActionController中的MethodNameResolver的初始化可知,

private MethodNameResolver methodNameResolver = new InternalPathMethodNameResolver()

  這個解析器提供從請求 URL 路徑解析處理器方法的方法名,從請求的最後一個路徑(/)開始,並忽略擴展名;如請求 URL 是「/user/list.html」 ,則解析的功能處
理方法名爲「list」 ,即調用 list 方法。該解析器還能夠指定前綴和後綴,經過 prefix 和 suffix 屬性來判斷。下面就是詳細的getHandlerMethodName方法的實現過程。

 

	public final String getHandlerMethodName(HttpServletRequest request)
			throws NoSuchRequestHandlingMethodException {

		String urlPath = this.urlPathHelper.getLookupPathForRequest(request);
		String name = getHandlerMethodNameForUrlPath(urlPath);
		if (name == null) {
			throw new NoSuchRequestHandlingMethodException(urlPath, request.getMethod(), request.getParameterMap());
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Returning handler method name '" + name + "' for lookup path: " + urlPath);
		}
		return name;
	}

  

5.總結說明

    MethodNameResolver 的實現是經過策略模式來的,經過這個也能夠是發現,Spring的實現中大量採用了設計模式的相關知識,若是Controller的實現中採用了模板設計模式同樣。若是本身可以靈活應用這些設計模式,而且有個很好的思想,我想寫出這樣優秀的代碼應該也不是問題,加油,共勉。
相關文章
相關標籤/搜索