JDK版本:1.7(最新更新) 春季:3.2.16-发布
我有一个通用的控制器类,可以重用于多种功能。由于基于注释的方法对此类需求的限制,我使用的是基于XML的配置。此外,我已禁用XML中的组件扫描。
我已经配置了同一个类的多个bean实例,并使用SimpleUrlHandlerMapping
将URL映射到控制器。如果我一次启用一个控制器测试项目,它可以正常工作。但是,当我启用第二个实例时,spring会抱怨以下错误:
ERROR: org.springframework.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'deviceController' bean method
public java.lang.String com.smvc.pr05.controllers.SearchController.search(java.util.Locale,org.springframework.ui.ModelMap)
to {[],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'searchController' bean method
public java.lang.String com.smvc.pr05.controllers.SearchController.search(java.util.Locale,org.springframework.ui.ModelMap) mapped.
...
Caused by: java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'installerController' bean method
public java.lang.String com.smvc.pr05.controllers.SearchController.search(java.util.Locale,org.springframework.ui.ModelMap)
to {[],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'deviceController' bean method
public java.lang.String com.smvc.pr05.controllers.SearchController.search(java.util.Locale,org.springframework.ui.ModelMap) mapped.
...
我已经尝试使用scope = singleton和scope = prototype作为控制器bean定义。我尝试过启用组件扫描(在XML中保持手动定义的bean)并禁用它。错误仍然存在。
虽然这可能是固定的,但如果我为每个实例创建具体类,我真的希望将它作为最后一个选项。我对Spring功能非常有信心,因为我在非控制器类中使用了类似的技术。
请让我知道,我错过了什么。
弹簧配置(已编辑,控制器为单件)
...
<beans:bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<beans:property name="mappings">
<beans:props>
<beans:prop key="/">homeController</beans:prop>
<beans:prop key="/deviceSearch/">deviceController</beans:prop>
<beans:prop key="/installerSearch/">installerController</beans:prop>
<beans:prop key="/customerSearch/">customerController</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
...
<beans:bean id="homeController" class="com.smvc.pr05.controllers.HomeController" >
</beans:bean>
<beans:bean id="deviceController" class="com.smvc.pr05.controllers.SearchController">
<beans:property name="metaModel" ref="deviceModel"/>
<beans:property name="searchService" ref="deviceService" />
</beans:bean>
<beans:bean id="installerController" class="com.smvc.pr05.controllers.SearchController" >
<beans:property name="metaModel" ref="installerModel"/>
<beans:property name="searchService" ref="installerService" />
</beans:bean>
<beans:bean id="customerController" class="com.smvc.pr05.controllers.SearchController" >
<beans:property name="metaModel" ref="customerModel"/>
<beans:property name="searchService" ref="customerService" />
</beans:bean>
Java控制器类:
...
@Controller
public class SearchController {
private static final Logger LOG = LoggerFactory.getLogger(SearchController.class);
private SearchService searchService; //Has explicit set() method
private MetaModel metaModel; //Has explicit set() method
@SuppressWarnings({ "unchecked" })
@RequestMapping(method = RequestMethod.POST)
public String search(Locale locale, ModelMap modelMap) {
...
}
public void setSearchService(SearchService searchService) {
this.searchService = searchService;
}
public void setMetaModel(MetaModel metaModel) {
this.metaModel = metaModel;
}
}
答案 0 :(得分:1)
主要问题是,当使用@Controller
和<mvc:annotation-driven />
时,RequestMappingHandlerMapping
和RequestMappingHandlerAdapter
将会启动。第一个将检测所有@Controller
注释bean并基于@RequestMapping
为它创建映射。
由于您已经注册了3个相同类型的bean,因此它将导致3个相同的映射,因此它会因为异常告诉您而停止。基本上,随着RequestMappingHandlerAdapter
/ RequestMappingHandlerMapping
的引入,使用SimpleUrlHandlerMapping
的能力和选择方法的注释方式都丢失了。
然而,您可以删除<mvc:annotation-driven />
并添加AnnotationMethodHandlerAdapter
但是该类或多或少已被弃用(并且至少在将来的Spring版本中将被删除)。
我建议使用旧的可信Controller
接口而不是注释。您只有一个想要使用的方法,因此使用旧的支持类是一个可行的选择。
public class SearchController extends AbstractController {
private static final Logger LOG = LoggerFactory.getLogger(SearchController.class);
private SearchService searchService; //Has explicit set() method
private MetaModel metaModel; //Has explicit set() method
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception;
if (!("post".equalsIgnoreCase(request.getMethod()))) {
return null; // or throw exception or ....
}
final Locale locale = LocaleContextHolder.getLocale(); // retrieve current locale.
ModelAndView mav = new ModelAndView("your-view");
// prepare your model instead of adding to ModelMap
mav.addObject("name", object);
return mav;
}
// Omitted setters.
}
当您升级到删除已弃用类的Spring版本时,这将阻止注释扫描进入,并使您免于重构(再次)。
答案 1 :(得分:0)
似乎是组件扫描仍在工作。因为有人根据SearchController
注释创建了@Controller
的实例。这就是你得到Cannot map 'deviceController' bean method
的原因。
另一个问题是,如果在xml配置中使用<mvc:annotation-driven/>
,mvc引擎将查找标有@Controller
注释的所有bean,并将尝试根据方法注释映射此bean。因为你有三个相同类的控制器,并且这个类标有@Controller
,mvc引擎将尝试映射所有这些控制器。由于它们将具有相同的方法注释,因此它们将映射到相同的路径(在您的情况下,它是空路径)。这就是你得到Cannot map 'installerController' bean method
的原因。
两种情况的解决方案:从@Controller
类中删除SearchController
注释。
答案 2 :(得分:0)
Controller只是一个刻板印象注释,与组件扫描结合使用。这里的罪魁祸首是@RequestMapping,它将所有方法映射到同一个url。要使配置正常工作,请删除<mvc:annotation-driven/>
元素,该元素注册一个RequestMappingHandlerMapping bean,该bean使用@RequestMapping进行url映射。现在将使用SimpleUrlHandlerMapping而不是通过mvc配置的那个:annotation-driven或@EnableWebMvc
但是你需要注册一个HandlerAdapter,它知道如何处理@RequestMapping方法,即org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
如下
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>