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

时间:2015-03-01 13:35:16

标签: java spring spring-mvc

我正在使用Spring4构建WebApplication。 WebApplication Dispatcher映射到/ app / *,因为我有第二个用于REST服务的Spring Dispatcher with mapping / services / *

当我尝试启动WebApplication时,Spring会抛出异常(找到模糊映射),因为我在两个不同的控制器中具有相同的映射(“/ persons”)。 这是正确的,我在WebApplication中的Controller中有这个映射,在RestController中有相同的映射。但控制器在不同的Dispatchers中具有不同的调度程序映射。 有没有办法解释Spring这个映射是否正确? 或者这是我自己的错误,我走的路是完全错误的?

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)

这是我的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/*");

}
}

这是我用于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/*");

}
}

以下是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;
    }

}

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



}

在我的WebApplication中,我有这个控制器:

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

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

在我的ServiceApplication中,我有一个RestController:

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

修改

我已删除了关于magnama建议的ContextLoaderListener。但错误实际上是一样的。

Here(Pastebin)是Spring的完整输出。我认为有些事情是非常错误的。 Spring首先从serviceapplication上下文开始,并注册/ home(来自HomeController),它是WebApplication的一部分。 HomeController在io.theoperator.controller包中,它是 ServiceApplicationConfiguration的ComponentScan的一部分...... 目前我不知道出了什么问题......

2 个答案:

答案 0 :(得分:0)

所有人的权利。
我发现了我的错误! 我创建了一个只有七个文件的新项目: 2个初始化器,2个配置,2个控制器和1个RestController,我再现了相同的情况 我的错误是将初始化程序和配置全部放在同一个程序包中,#i ;.operator.configuration&#34;。
分裂为&#34; io.theoperator.configuration.web&#34;和&#34; io.theoperator.configuration.service&#34;并在配置中调整ComponentScan,服务应用程序仅映射RestControllers

我在github上创建了一个存储库:Project foo on github.com
主分支是工作分支和分支错误,具有不明确的映射错误。

特别感谢Magnamag和Pavel的双重扫描提示! ;)

问候 jomikel

答案 1 :(得分:0)

正在寻找解决我遇到的同样问题,在阅读完帖后得到了一个解决方案,没有将配置文件拆分到不同的文件夹。

主要思路:当您为每个servlet显式提供配置类时 - 在配置类中删除 @Configuration 注释以避免混乱。

假设我们有一个应用程序,提供与用户数据库一起使用的WEB和REST服务。 因此,REST端点为http://blah-blah-blah/rest和web - http://blah-blah-blah/web。 在这两种情况下,当我们想要获得时,即用户列表 - 我们分别访问... / rest / users或... / web / users;对于选定的用户地址,将是... / rest / users / 1或... / web / users / 1等。 App init类和配置类在同一个包中,REST和WEB控制器分别在rest和web包中

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的配置:

@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/");
}
}

休息-配置:

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

}

网络控制器:

@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...

}

其余控制器:

@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...
}

希望它能帮助别人,至少......