在介紹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
MethodNameResolver是個接口,來看看定義web
public interface MethodNameResolver { String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException; }
這個接口用於MultiActionController方法中獲取具體的參數,它是經過策略模式來實現的。它能夠經過具體的請求映射處處理的方法名上,他們的改變也不會影響到其餘應用程序。看看下面其餘的具體實現。MethodNameResolver的實現類包括ParameterMethodNameResolver、AbstractUrlMethodNameResolver、InternalPathMethodNameResolver、PropertiesMethodNameResolver。spring
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和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>
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; }
MethodNameResolver 的實現是經過策略模式來的,經過這個也能夠是發現,Spring的實現中大量採用了設計模式的相關知識,若是Controller的實現中採用了模板設計模式同樣。若是本身可以靈活應用這些設計模式,而且有個很好的思想,我想寫出這樣優秀的代碼應該也不是問題,加油,共勉。