Spring之SpringMVC的MethodNameResolver(源码)分析

前言

  在介绍SpringMVC  的Controller的具体实现中,我们讲到了MultiActionController。在获取处理请求对于的方法的时候我们用到了下面的代码,来自于MultiActionController的handleRequestInternal的方法:

	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具体实现。

1.MethodNameResolver介绍

MethodNameResolver是个接口,来看看定义

public interface MethodNameResolver {

	String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException;

}

  这个接口用于MultiActionController方法中获取具体的参数,它是通过策略模式来实现的。它可以通过具体的请求映射到处理的方法名上,他们的改变也不会影响到其他应用程序。看看下面其他的具体实现。MethodNameResolver的实现类包括ParameterMethodNameResolver、AbstractUrlMethodNameResolver、InternalPathMethodNameResolver、PropertiesMethodNameResolver。

2.ParameterMethodNameResolver

ParameterMethodNameResolver实现了MethodNameResolver接口,它支持通过参数值来映射到方法名上。通过注释知道可以通过下面的过程来获取具体的方法:

  1.根据请求的参数名解析功能方法名;

  2.根据请求参数名的值解析功能方法名,默认的参数名是 action,即请求的参数中含有“action=query” ,则功能处理方法名为 query;

  3.逻辑功能方法名到真实功能方法名映射;

  4.默认的方法名,当以上策略失败时默认调用的方法名。

来具体获取处理器方法名过程:

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

  3、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的实现中采用了模板设计模式一样。如果自己能够灵活应用这些设计模式,并且有个很好的思想,我想写出这样优秀的代码应该也不是问题,加油,共勉。
posted on 2015-11-06 07:44  叼烟斗的纤夫  阅读(1043)  评论(0编辑  收藏  举报