简体   繁体   English

Spring MVC:与多个Dispatchers一起发现的模糊映射

[英]Spring MVC: Ambiguous mapping found with multiple Dispatchers

i'm building a WebApplication with Spring4. 我正在使用Spring4构建WebApplication。 The WebApplication Dispatcher is mapped to /app/* because i have a second Spring Dispatcher for REST Services with mapping /services/* WebApplication Dispatcher映射到/ app / *,因为我有第二个用于REST服务的Spring Dispatcher with mapping / services / *

When i try to start the WebApplication Spring throws Exceptions (Ambiguous mapping found) because i have the same mapping ("/persons") in two different Controllers. 当我尝试启动WebApplication Spring抛出异常(发现模糊映射),因为我在两个不同的控制器中有相同的映射(“/ persons”)。 It is correct, i have this mapping in a Controller in my WebApplication and the same mapping in a RestController. 这是正确的,我在WebApplication中的Controller中有这个映射,在RestController中有相同的映射。 But the Controllers are in different Dispatchers with the different dispatcher mappings. 但控制器在不同的Dispatchers中具有不同的调度程序映射。 Is there a way to explain Spring that this mappings are correct? 有没有办法解释Spring这个映射是否正确? Or is it a mistake from myself and the way i go is complete wrong? 或者这是我自己的错误,我走的路是完全错误的?

Output from Jetty (Stripped some unneeded things): Jetty的输出(删除一些不需要的东西):

INFO: Root WebApplicationContext: initialization started
INFO: Mapped "{[/home],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView io.theoperator.controller.Home.showMessage()
INFO: Mapped "{[/home],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView io.theoperator.controller.Home.showMessage()
INFO: Mapped "{[/persons],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView io.theoperator.controller.PersonController.list()
INFO: Mapped "{[/persons],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView io.theoperator.controller.PersonController.save(io.theoperator.model.Person)
INFO: Root WebApplicationContext: initialization completed in 1319 ms
INFO: FrameworkServlet 'serviceapplication': initialization started
INFO: Mapped "{[/persons/page],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.util.Map<java.lang.String, java.lang.Object> io.theoperator.restservice.PersonServiceController.getPage(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
INFO: Mapped "{[/persons],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public io.theoperator.restservice.PersonServiceController$PersonList io.theoperator.restservice.PersonServiceController.getAll()
INFO: Mapped "{[/persons/{id}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public io.theoperator.model.Person io.theoperator.restservice.PersonServiceController.getPerson(java.lang.String)
Mar 01, 2015 12:36:03 PM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod
INFO: Mapped "{[/persons/page],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.util.Map<java.lang.String, java.lang.Object> io.theoperator.restservice.PersonServiceController.getPage(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
Mar 01, 2015 12:36:03 PM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod
INFO: Mapped "{[/persons],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public io.theoperator.restservice.PersonServiceController$PersonList io.theoperator.restservice.PersonServiceController.getAll()
Mar 01, 2015 12:36:03 PM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod
INFO: Mapped "{[/persons/{id}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public io.theoperator.model.Person io.theoperator.restservice.PersonServiceController.getPerson(java.lang.String)
Mar 01, 2015 12:36:03 PM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod
INFO: Mapped "{[/home],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView io.theoperator.controller.Home.showMessage()
Mar 01, 2015 12:36:03 PM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod
INFO: Mapped "{[/persons/{id}],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView io.theoperator.controller.PersonController.details(java.lang.String)
Mar 01, 2015 12:36:03 PM org.springframework.web.context.support.AnnotationConfigWebApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'personController' bean method 
public org.springframework.web.servlet.ModelAndView io.theoperator.controller.PersonController.list()
to {[/persons],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'personServiceController' bean method
public io.theoperator.restservice.PersonServiceController$PersonList io.theoperator.restservice.PersonServiceController.getAll() mapped.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:663)
    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:535)
    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:489)
    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
    at javax.servlet.GenericServlet.init(GenericServlet.java:244)
    at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:613)
    at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:396)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:341)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1343)
    at org.eclipse.jetty.maven.plugin.JettyWebAppContext.startWebapp(JettyWebAppContext.java:296)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1336)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:742)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:499)
    at org.eclipse.jetty.maven.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:365)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:399)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:366)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:516)
    at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:359)
    at org.eclipse.jetty.maven.plugin.JettyRunMojo.execute(JettyRunMojo.java:167)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

This is my WebApplicationInitializer for the WebApplication: 这是我的WebApplication的WebApplicationInitializer:

public class WebApplicationInitializer implements org.springframework.web.WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {

    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(WebApplicationConfiguration.class);

    container.addListener(new ContextLoaderListener(rootContext));

    ServletRegistration.Dynamic dispatcher = container.addServlet("webapplication", new DispatcherServlet(rootContext));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/app/*");

}
}

and this is my WebApplicationInitializer for REST Services 这是我用于REST服务的WebApplicationInitializer

public class ServiceApplicationInitializer implements org.springframework.web.WebApplicationInitializer {

@Override
public void onStartup(ServletContext container) {

    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(ServiceApplicationConfiguration.class);

    ServletRegistration.Dynamic dispatcher = container.addServlet("serviceapplication", new DispatcherServlet(rootContext));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/services/*");

}
}

Here are the Configurations for the Dispatchers: 以下是调度员的配置:

@EnableWebMvc
@ComponentScan(basePackages = {
        "io.theoperator.controller",
        "io.theoperator.service",
        "io.theoperator.repository",
        "io.theoperator.configuration",
})
@Configuration
public class WebApplicationConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Bean
    public InternalResourceViewResolver getInternalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/pages/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

}

and

@EnableWebMvc
@ComponentScan(basePackages = {
        "io.theoperator.service",
        "io.theoperator.repository",
        "io.theoperator.configuration",
        "io.theoperator.restservice"
})
public class ServiceApplicationConfiguration extends WebMvcConfigurerAdapter {



}

In my WebApplication i have this Controller: 在我的WebApplication中,我有这个控制器:

@Controller
@RequestMapping("/persons")
public class PersonController {

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView list() {
        return new ModelAndView("persons/list");
    }
}

In my ServiceApplication i have a RestController: 在我的ServiceApplication中,我有一个RestController:

@RestController
@RequestMapping("/persons")
public class PersonServiceController {
@RequestMapping(method = RequestMethod.GET)
    public PersonList getAll() {
        return new PersonList(this.personService.list());
    }
}

Edit 编辑

I have removed the ContextLoaderListener how magnama suggested. 我已经删除了ContextLoaderListener magnama建议的方式。 But the error is effectively the same. 但错误实际上是一样的。

Here (Pastebin) is the complete output from Spring. 这里 (Pastebin)是Spring的完整输出。 I think something goes very wrong. 我认为有些事情是非常错误的。 Spring starts at first the serviceapplication context and registers /home (From HomeController) that is part of the WebApplication. Spring首先从serviceapplication上下文开始,并注册/ home(来自HomeController),它是WebApplication的一部分。 The HomeController is in package io.theoperator.controller which is not part of the ComponentScan of the ServiceApplicationConfiguration... At the moment i have no idea what is wrong... HomeController是在io.theoperator.controller包中,它不是 ServiceApplicationConfiguration的ComponentScan的一部分...目前我不知道有什么问题...

Thx for all. 感谢所有人。
I have found my error! 我发现了我的错误!
I have created a new project with only seven files: 2 Initializers, 2 Configurations, 2 Controllers and 1 RestController and i have reproduced the same situation. 我创建了一个只有七个文件的新项目:2个初始化器,2个配置,2个控制器和1个RestController,我已经重现了相同的情况。
My error was to put the Initializers and Configurations all in the same package "io.theoperator.configuration". 我的错误是将Initializers和Configurations全部放在同一个包“io.theoperator.configuration”中。
After splitting to "io.theoperator.configuration.web" and "io.theoperator.configuration.service" and adjusting the ComponentScan in the Configurations the Service Application maps only the RestControllers 拆分为“io.theoperator.configuration.web”和“io.theoperator.configuration.service”并调整Configurations中的ComponentScan之后,Service Application仅映射RestControllers

I have created a repository on github: Project foo on github.com 我在github上创建了一个存储库: github.com上的Project foo
The master branch is the working one and branch error with the Ambiguous mapping error. 主分支是工作分支和分支错误,具有不明确的映射错误。

Special thanks to Magnamag and Pavel for their tip with the double scanning! 特别感谢Magnamag和Pavel的双重扫描技巧! ;) ;)

regards jomikel 关于jomikel

was looking to solve the same problem I had and after reading your post got a solution WITHOUT splitting config files to different folders. 我正在寻找解决我遇到的同样问题,并在阅读你的帖子后得到一个解决方案没有将配置文件拆分到不同的文件夹。

Main idea: as you explicitly provide config class for each servlet - remove @Configuration annotation in config classes to avoid mess. 主要思想:当您为每个servlet显式提供配置类时 - 在配置类中删除@Configuration注释以避免混乱。

Let's assume we have an app, providing WEB and REST services which is working with User database. 假设我们有一个应用程序,提供与用户数据库一起使用的WEB和REST服务。 So REST endpoint would be http://blah-blah-blah/rest and web - http://blah-blah-blah/web . 所以REST端点是http:// blah-blah-blah / rest和web - http:// blah-blah-blah / web In both cases, when we want to get, ie, list of users - we access, respectively, .../rest/users or .../web/users; 在这两种情况下,当我们想要获得时,即用户列表 - 我们分别访问... / rest / users或... / web / users; for selected user address would be .../rest/users/1 or .../web/users/1, etc. App init class and config classes are in same package, REST and WEB controllers are in rest and web packages, respectively 对于选定的用户地址将是... / rest / users / 1或... / web / users / 1等.App init类和配置类在同一个包中,REST和WEB控制器在rest和web包中,分别

App init: App init:

public class AppInitializer implements WebApplicationInitializer  {

@Override
public void onStartup(ServletContext container) throws ServletException {
    // Create the 'root' Spring application context
      AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
      rootContext.register(RootConfig.class);
      //data access layer
      rootContext.register(DataConfig.class);
      // Manage the lifecycle of the root application context
      container.addListener(new ContextLoaderListener(rootContext));

      //--------WEB--------------         

      // Create web dispatcher servlet's application context
      AnnotationConfigWebApplicationContext webDispatcherContext = 
              new AnnotationConfigWebApplicationContext();
      webDispatcherContext.register(WebDispatcherConfig.class);

      // Register and map web dispatcher servlet
      ServletRegistration.Dynamic webDispatcher =
              container.addServlet("webDispatcher", new DispatcherServlet(webDispatcherContext));
      webDispatcher.setLoadOnStartup(1);
      webDispatcher.addMapping("/web/*");

      //--------REST-------------        

      // Create rest dispatcher servlet's application context
      AnnotationConfigWebApplicationContext restDispatcherContext = 
              new AnnotationConfigWebApplicationContext();
      restDispatcherContext.register(RESTDispatcherConfig.class);

      // Register and map rest dispatcher servlet
      ServletRegistration.Dynamic restDispatcher =
              container.addServlet("restDispatcher", new DispatcherServlet(restDispatcherContext));
      restDispatcher.setLoadOnStartup(1);
      restDispatcher.addMapping("/rest/*");

}

web-config: 网络配置:

@EnableWebMvc
@ComponentScan("io.github.d2edev.mywebapp.web")
public class WebDispatcherConfig extends WebMvcConfigurerAdapter {

@Bean
public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/jsp/");
    resolver.setSuffix(".jsp");
    resolver.setExposeContextBeansAsAttributes(true);
    return resolver;
}



@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}

rest-config: 休息-配置:

@EnableWebMvc
@ComponentScan("io.github.d2edev.mywebapp.rest")
public class RESTDispatcherConfig {

}

web-controller: 网络控制器:

@Controller
@RequestMapping("/users")
public class UserController {

private UserRepository userRepository;

@Autowired
public UserController(UserRepository userRepository) {
    this.userRepository=userRepository;
}

//list all
@RequestMapping(method=RequestMethod.GET)
public String listAllUsers(Model model){
    model.addAttribute("userList", userRepository.findAll());
    return "user/users";
}

//show registration form
@RequestMapping(value="/newUser", method=RequestMethod.GET)
public String showRegistrationForm(){
    return "user/newUser";
}

//other methods here...

}

rest-controller: 休息控制器:

@RestController
@RequestMapping("/users")
public class UserControllerREST {

// data source
private UserService userService;

@Autowired
public UserControllerREST(UserService userService) {
    this.userService = userService;
}

// get all
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<User>> getAllUsers() {
    List<User> users = userService.getAll();
    if (users.isEmpty()) {
        return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<List<User>>(users, HttpStatus.OK);

}

//other methods to follow...
}

Hope it helps others, at least... 希望它能帮助别人,至少......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM