这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的时候是支持多态的,突然被这东西给弄懵了,由于小弟才疏学浅,一直未能思考出个所以然来,望高手解答。小弟感激不尽!
分享到:
相关推荐
基于SpringMVC annotation 的图形验证码 主要是controller的写法 实现前台验证码的显示 和刷新 验证功能相信大家都能写出来 访问 http://localhost:8080/SpringMVC_annotation_volidate/login jsp页面关键代码 ...
标题 "springmvc初探(annotation)" 暗示了我们将探讨Spring MVC框架的注解驱动编程。Spring MVC是Spring框架的一部分,专门用于构建Web应用程序。它提供了模型-视图-控制器(MVC)架构,使开发者可以更有效地组织和...
@Resource 并不是 Spring 的注解,它的包是 javax.annotation.Resource,需要导入,但是 Spring 支持该注解的注入。 * 共同点:两者都可以写在字段和 setter 方法上。两者如果都写在字段上,那么就不需要再写setter...
在Spring MVC框架中,JSON(JavaScript Object Notation)支持是必不可少的,因为它允许应用程序与客户端进行数据交换,尤其是在构建RESTful服务时。JSON格式轻便、易于读写,且被广泛接受为网络通信的标准数据格式...
1、访问地址:... spring-webmvc-2.5.6.jar不支持@PathVariable注解,参数必须采用传统方式的?传递;3以上版本就支持@PathVariable注解,允许的话,建议采用SpringMVC4,增加了一些新特性 ;
在IT行业中,SpringMVC和MyBatis是两个非常重要的框架,它们被广泛用于构建企业级的Java Web应用。本文将深入探讨如何使用注解方式搭建基于SpringMVC和MyBatis的框架,并通过JUnit进行测试。 首先,SpringMVC是...
SpringMVC配置控制器有两种方式,常用的是通过WEB-INF下的[servlet-name]-servlet.xml,本项目采用的是另外一种通过配置配置文件路径的方式来实现Springmvc的配置。
SpringMVC是一个基于Java的轻量级Web应用框架,它为构建RESTful应用程序提供了强大的支持。在"SpringMVC-Annotation"的例子中,我们将重点探讨如何利用注解来简化SpringMVC的配置和控制器的实现。 1. **注解驱动的...
1、访问地址:... spring-webmvc-2.5.6.jar不支持@PathVariable注解,参数必须采用传统方式的?传递;3以上版本就支持@PathVariable注解,允许的话,建议采用SpringMVC4,增加了一些新特性 ;
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。 1、共同点 两者都可以写在字段和setter方法上。两者...
SpringMVC通过其强大的功能和对Ajax的支持,使得开发者可以轻松地构建这样的应用。 Ajax的核心在于创建异步请求,从服务器获取数据并动态更新页面,而无需完全刷新整个页面。在SpringMVC中,这一过程可以通过使用...
在"SpringMVC-annotation"这个主题中,我们将深入探讨如何利用注解来实现SpringMVC的配置,从而极大地减少XML配置文件的工作量。 在传统的SpringMVC应用中,我们需要在`web.xml`中配置DispatcherServlet,并在...
springmvc2.5.6实现webservice 接口 带参数... spring-webmvc-2.5.6.jar不支持@PathVariable注解,参数必须采用传统方式的?传递;3以上版本就支持@PathVariable注解,允许的话,建议采用SpringMVC4,增加了一些新特性
但是,在某些情况下,CharacterEncodingFilter 并不能完全解决问题。这是因为Tomcat 的默认编码方式可能不是UTF-8。因此,我们需要修改Tomcat 的配置文件server.xml。在server.xml文件中,添加以下配置: ```xml ...
1. **什么是SpringMVC?** SpringMVC是Spring框架的一部分,它提供了一个用于处理HTTP请求和响应的模型-视图-控制器(MVC)架构。通过将业务逻辑、数据处理和用户界面分离,它简化了Web应用程序的开发。 2. **...
springMVC使用注解,事务不失效 直接导入myeclipse,在mysql的test数据库中新建个user表、字段属性如下 username varchar 20 userpwd varchar 20
springmvc加入json支持jar包整理,jackson-annotations-2.4.0.jar、jackson-core-2.4.2.jar、jackson-databind-2.4.2.jar
《SpringMVC注解详解与应用》 SpringMVC作为Java Web开发中广泛使用的轻量级框架,极大地简化了Web应用程序的构建。其中,注解的使用是SpringMVC的一大特色,它使得代码更加简洁、易读,同时也提高了开发效率。下面...
SpringMVC 提供了对文件上传和下载的支持,可以方便地处理用户的文件操作请求。 十一、RESTful API 设计 SpringMVC 也支持 RESTful 风格的 Web 服务,可以通过 @RequestMapping 注解配合 HTTP 方法(GET、POST、PUT...
在IT行业中,SpringMVC、Spring和MyBatis是三个非常重要的Java开发框架,它们各自在Web应用的各个层面上发挥着关键作用。本项目是一个整合了这三个框架的基于Annotation和Maven的项目,旨在提供一种高效、灵活的开发...