`

SpringMVC Annotation不支持多态?

阅读更多
这2天在一直在学习SpringMVC,也是才接触这个东西,先前使用XML配置一直配不通,后来就想着使用Spring的注解形式来学习SpringMVC,本身注释就比较简单吧,但是注释也有一定的缺陷,这里就不谈了。

先贴上我的代码,这样来分析比较直观一些,看一下Controller : RegistController.java
@Controller
@RequestMapping(value = "/regist.action")
public class RegistController {

	@Resource(name = "hibernateTemplateServiceImpl")
	private HibernateTemplateService hibernateServiceImpl;

	@RequestMapping(params = "method=findStu", method = RequestMethod.POST)
	public ModelAndView findStu(@RequestParam("stuId") int stuId, ModelMap map) {
		System.out.println("stuid is : " + stuId);
		return findStu(stuId, null, map);;
	}

	@RequestMapping(params = "method=findStu", method = RequestMethod.POST)
	public ModelAndView findStu(@RequestParam("stuId") int stuId, @RequestParam("stuName") String stuName, ModelMap map) {
		System.out.println("stuid is : " + stuId + " and stuname is :" + stuName);
		String hql = "";
		Student stu = new Student();
		if (0 != stuId) {
			if (null != stuName || "".equals(stuName)) {
				hql = "from Student where stuId=" + stuId + " and stuName='" + stuName + "'";
			} else {
				hql = "from Student where stuId=" + stuId;
			}
			stu = (Student) hibernateServiceImpl.findAllStu(hql).get(0);
			return new ModelAndView("students", "stu", stu);
		} else
			return new ModelAndView("error");
	}
}

看一下这个2个方法,采用了面向对象的多态,注释@RequestMapping映射的地址方法均是一致的,本是抱着一种好奇的心态去做,因为在处理业务逻辑中常常会运用到多态的思想,所以想试着去做,看能否成功。令人失望的是报错了,开始一直以为是自己哪里没有弄得对,查看报错信息,发现是Spring中的AnnotationMethodHandlerAdapter这个类捣的鬼。


当客户端向服务器端发送请求时,测试页面:findByIdStu.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
	<head>
	</head>

	<body>
	<form action="regist.action?method=findStu" method="post">
		学号 : <input name = "stuId"><br/>
		<input value="submit" type="submit">
	</form>
		<br>
	</body>
</html>



tomcat回馈给我的信息为:
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path '/regist.action': {public org.springframework.web.servlet.ModelAndView com.action.RegistController.findStu(int,org.springframework.ui.ModelMap), public org.springframework.web.servlet.ModelAndView com.action.RegistController.findStu(int,java.lang.String,org.springframework.ui.ModelMap)}. If you intend to handle the same path in multiple methods, then factor them out into a dedicated handler class with that path mapped at the type level!
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.resolveHandlerMethod(AnnotationMethodHandlerAdapter.java:600)

于是跟踪错误信息,找到了AnnotationMethodHandlerAdapter类下的内部类ServletHandlerMethodResolver下的resolveHandlerMethod方法,此方法主要是分析客户端发来的请求,找到相应的Controller,再寻找处理方法。看一下resolveHandlerMethod的一段代码。
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
			Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);
			Map<RequestSpecificMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestSpecificMappingInfo, Method>();
			Set<String> allowedMethods = new LinkedHashSet<String>(7);
			String resolvedMethodName = null;
			for (Method handlerMethod : getHandlerMethods()) {
				RequestSpecificMappingInfo mappingInfo = new RequestSpecificMappingInfo(this.mappings.get(handlerMethod));
				boolean match = false;
				if (mappingInfo.hasPatterns()) {
					for (String pattern : mappingInfo.getPatterns()) {
						if (!hasTypeLevelMapping() && !pattern.startsWith("/")) {
							pattern = "/" + pattern;
						}
						String combinedPattern = getCombinedPattern(pattern, lookupPath, request);
						if (combinedPattern != null) {
							if (mappingInfo.matches(request)) {
								match = true;
								mappingInfo.addMatchedPattern(combinedPattern);
							}
							else {
								if (!mappingInfo.matchesRequestMethod(request)) {
									allowedMethods.addAll(mappingInfo.methodNames());
								}
								break;
							}
						}
					}
					mappingInfo.sortMatchedPatterns(pathComparator);
				}
				else {
					// No paths specified: parameter match sufficient.
					match = mappingInfo.matches(request);
					if (match && mappingInfo.getMethodCount() == 0 && mappingInfo.getParamCount() == 0 &&
							resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
						match = false;
					}
					else {
						if (!mappingInfo.matchesRequestMethod(request)) {
							allowedMethods.addAll(mappingInfo.methodNames());
						}
					}
				}
				if (match) {
					Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
					if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
						if (methodNameResolver != null && !mappingInfo.hasPatterns()) {
							if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
								if (resolvedMethodName == null) {
									resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
								}
								if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
									oldMappedMethod = null;
								}
								if (!resolvedMethodName.equals(handlerMethod.getName())) {
									if (oldMappedMethod != null) {
										targetHandlerMethods.put(mappingInfo, oldMappedMethod);
										oldMappedMethod = null;
									}
									else {
										targetHandlerMethods.remove(mappingInfo);
									}
								}
							}
						}
						if (oldMappedMethod != null) {
							throw new IllegalStateException(
									"Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
									oldMappedMethod + ", " + handlerMethod +
									"}. If you intend to handle the same path in multiple methods, then factor " +
									"them out into a dedicated handler class with that path mapped at the type level!");
						}
					}
				}
			}

这一段代码主要是分析请求的Controller下的RequestMapping映射关系,找到相应的处理方法,再交给接下来的代码来进行分发。然而出现问题的恰恰就是这段代码。其中有一段是:
if (match) {
					Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
					if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
						if (methodNameResolver != null && !mappingInfo.hasPatterns()) {
							if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
								if (resolvedMethodName == null) {
									resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
								}
								if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
									oldMappedMethod = null;
								}
								if (!resolvedMethodName.equals(handlerMethod.getName())) {
									if (oldMappedMethod != null) {
										targetHandlerMethods.put(mappingInfo, oldMappedMethod);
										oldMappedMethod = null;
									}
									else {
										targetHandlerMethods.remove(mappingInfo);
									}
								}
							}
						}
						if (oldMappedMethod != null) {
							throw new IllegalStateException(
									"Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
									oldMappedMethod + ", " + handlerMethod +
									"}. If you intend to handle the same path in multiple methods, then factor " +
									"them out into a dedicated handler class with that path mapped at the type level!");
						}
					}
				}

正是这段代码验证了Controller层中是否存在相同名字的Method,如果存在,则抛出异常,否则继续执行下面的代码。

第一次扫描
然后我采用Debug来一步步的追踪,当Spring在扫描处理器中的Method的时候,首先会找到findStu(int,ModelMap)这个方法,然后将它put进targetHandlerMethods中,并返回其key为mappingInfo的值,由于targetHandlerMethods中并不存在mappingInfo这个key,所以返回null。一步步执行下去,执行完后继续循环。

第二次扫描
于是第二次循环开始了,这一次扫描到的方法是findStu(int,String,ModelMap)仍然执行到那一步,这时再一次向targetHandlerMethods.put(mappingInfo, handlerMethod)值时,这次在targetHandlerMethods中存在mappingInfo这个key了,于是返回先前的方法findStu(int,MdelMap),然后进行一步步的判断。
if (methodNameResolver != null && !mappingInfo.hasPatterns()) 

这一步已经通过,继续转接下一步。

if (!oldMappedMethod.getName().equals(handlerMethod.getName())) 

这一步发现oldMappedMethod的name与handlerMethod的name是相同的,于是就不再执行下去,执行下一个if语句。

if (oldMappedMethod != null){
							throw new IllegalStateException(
									"Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
									oldMappedMethod + ", " + handlerMethod +
									"}. If you intend to handle the same path in multiple methods, then factor " +
									"them out into a dedicated handler class with that path mapped at the type level!");
						}

发现oladMappedMethod并不为空的,所以就抛出了异常。

通过了一晚上的时间去读代码,思考。一直没弄明白为什么作者要去这样设计,这样设计的好处在哪里,以前在使用Struts2的时候是支持多态的,突然被这东西给弄懵了,由于小弟才疏学浅,一直未能思考出个所以然来,望高手解答。小弟感激不尽!
分享到:
评论
2 楼 lrj2u 2015-08-28  
xuhuan1108 写道
请问找到解决方案没有?


解决方法就是不要写两个一样名字的方法,还有一样的匹配uri,最近也遇到过这种情况,搜到这篇文章。
1 楼 xuhuan1108 2014-12-18  
请问找到解决方案没有?

相关推荐

Global site tag (gtag.js) - Google Analytics