Spring 在ServletContext和ContextLoaderListener中组织bean

Spring 在ServletContext和ContextLoaderListener中组织bean,spring,spring-mvc,Spring,Spring Mvc,我有一个SpringWebMVC+spring安全应用程序,我遇到了一个bean注入问题。首先,org.springframework.web.servlet.DispatcherServlet、org.springframework.web.filter.DelegatingFilterProxy和org.springframework.web.context.ContextLoaderListener都是在web.xml中定义的,servlet上下文称为servlet context.xml

我有一个SpringWebMVC+spring安全应用程序,我遇到了一个bean注入问题。首先,
org.springframework.web.servlet.DispatcherServlet
org.springframework.web.filter.DelegatingFilterProxy
org.springframework.web.context.ContextLoaderListener
都是在
web.xml
中定义的,servlet上下文称为
servlet context.xml
和应用程序上下文--
application context.xml

现在,当我创建使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
的服务时,问题就出现了。如果我在
application context.xml
中创建这个bean,则找不到
RequestMappingHandlerMapping
类型的bean。但是,如果我把它放在
servlet context.xml
中,找到了
RequestMappingHandlerMapping
,我可以在控制器中使用我的服务,但不能在
application context.xml
中创建的其他服务中使用它

我的问题是如何在这种应用程序配置中组织bean创建。我觉得我错过了一些简单的东西

编辑:当应用程序以特定配置文件启动时,过滤器链中会添加一个过滤器,允许您模拟API调用。这个过滤器是在应用程序上下文中定义的,因为在应用程序上下文中定义了spring安全性。它还需要访问一个服务,该服务可以提供所有请求映射的列表以及与之相关的自定义权限(不,它们不能很好地转换为spring角色)。但是,这个服务需要在servlet上下文中定义,因为它需要访问
RequestMappingHandlerMapping

编辑#2:我在这里生成了一个MWE(可能应该是最小的不工作示例?)

现在,有一个
ExampleController
CoreService
。它们都是在不同的上下文中创建的,需要访问
mappedursservice
,而这反过来又要求
RequestMappingHandlerMapping
bean是可访问的。根据实际创建
MappedUrlsService
bean的位置,您可能会发现
RequestMappingHandlerMapping
bean未找到,或者
MappedUrlsService
bean未找到。以下是未找到
RequestMappingHandlerMapping
时的堆栈跟踪:

SEVERE: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.github.guilty.spring.service.CoreServiceImpl#0': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.github.guilty.spring.service.MappedUrlsService com.github.guilty.spring.service.CoreServiceImpl.mappedUrlsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.github.guilty.spring.service.MappedUrlsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
        at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
        at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
        at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:800)
        at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:446)
        at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:792)
        at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:296)
        at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1341)
        at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1334)
        at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:744)
        at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:497)
        at org.eclipse.jetty.maven.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:281)
        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:60)
        at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:154)
        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:60)
        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:357)
        at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
        at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:60)
        at org.eclipse.jetty.server.Server.doStart(Server.java:324)
        at org.eclipse.jetty.maven.plugin.JettyServer.doStart(JettyServer.java:68)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
        at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:564)
        at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:360)
        at org.eclipse.jetty.maven.plugin.JettyRunMojo.execute(JettyRunMojo.java:168)
        at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:133)
        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:108)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:76)
        at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
        at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:116)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:361)
        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:213)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:157)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        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)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.github.guilty.spring.service.MappedUrlsService com.github.guilty.spring.service.CoreServiceImpl.mappedUrlsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.github.guilty.spring.service.MappedUrlsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
        ... 63 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.github.guilty.spring.service.MappedUrlsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1103)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:963)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
        ... 65 more

使用
mvn jetty:Run
运行该示例。请注意,运行它需要
Java7
Maven

RequestMappingHandlerMapping设计为仅在控制器层中使用(通常与
一起使用),因此应仅在servlet-context.xml中使用


您在服务层中需要它有什么原因吗?

DispatcherServlet
的上下文中创建了一个
RequestMappingHandlerMapping
,它可以访问根上下文(application context.xml)中定义的bean,但不能反过来访问。但是,您可以在根上下文中定义
RequestMappingHandlerMapping
bean。SpringMVC应该毫无问题地接受这一点。但是,在servlet上下文之外,它真的没有意义。

我不会讨论bean组织的原因,而只讨论如何

您可以将根上下文中的bean注入servlet上下文中的bean,但不能相反,因为servlet上下文是作为根上下文作为其父上下文创建的

依我看,在服务层中有一个bean依赖于控制器层中的一个bean是非常糟糕的设计,在现实世界中,你们应该尽量避免它。但让我们继续

我建议您在控制器包和servlet上下文中创建一个中继bean,并将
mappedursservice
保留在服务包和根上下文中。然后将服务注入中继,但实际上在服务中写入指向中继的指针:

public class MappedUrlsRelayImpl {

    @SuppressWarnings("SpringJavaAutowiringInspection")
    @Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping;

    // all the stuff that is presently in MappedUrlsServiceImpl
    // ...

    @Autowired
    private void setMappedUrlsServiceImpl (MappedUrlsServiceImpl serviceImpl) {
        serviceImpl.setMappedUrlsRelayImpl(this);
    }
}
您的服务bean的功能很简单:

public class MappedUrlsServiceImpl implements MappedUrlsService {

    @SuppressWarnings("SpringJavaAutowiringInspection")
    private MappedUrlsRelayImpl relay;

    @Override
    public Set<String> getMappedUrls() {
        return relay.getMappedUrls();
    }

    public void setMappedUrlsRelayImpl(MappedUrlsRelayImpl relay) {
        this.relay = relay;
    }
}
公共类MappedUrlsServiceImpl实现MappedUrlsService{
@抑制警告(“SpringJavaAutowiringInspection”)
专用MappedUrlsRelayImpl中继;
@凌驾
公共集getMappedUrls(){
返回relay.getMappedUrls();
}
公共无效setMappedUrlsRelayImpl(MappedUrlsRelayImpl中继){
这个继电器=继电器;
}
}
这很难看,因为服务依赖于控制器层类,我逆转了依赖性的Spring注入,但它是有效的

在servlet上下文中完成bean

<bean class="com.github.guilty.spring.controller.ExampleController" />
<bean class="com.github.guilty.spring.controller.MappedUrlsRelayImpl" />

在根上下文中

<bean class="com.github.guilty.spring.service.CoreServiceImpl" />
<bean class="com.github.guilty.spring.service.MappedUrlsServiceImpl" />


我认为“为什么”在这里并不重要。基本上,如果存在一个将servlet上下文和应用程序上下文合并为一个上下文的非魔法解决方案,那就太好了。@usk。有,但你不想这么做。您应该按预期使用Spring。@SotiriosDelimanolis:我更新了我的问题,指定了问题实际发生的时间。好了,现在我了解了您的用例。您能发布丢失的bean异常的完整堆栈跟踪以及配置的相关部分吗?@SotiriosDelimanolis:我提供了一个堆栈跟踪和一个应用程序来演示我的问题。请参阅我对原始问题的第二次编辑。这种方法似乎不起作用。我扩展了RequestMappingHandlerMapping,并将bean放在根上下文中。问题是,当它被创建时,它会在上下文中查询在
@Controller
s中定义的处理程序方法,而它们还没有被创建,因为它们是在servlet上下文中定义的。相关的代码可以找到啊是的。我认为这还不够好。既然您需要dispatcherservlet上下文中的bean,为什么不在那里定义过滤器bean呢?因为spring安全性是在核心上下文中配置的。它应该在核心上下文中定义,因为
org.springframework.web.context.ContextLoaderListener
创建了它,并且
org.springframework.web.filter.DelegatingFilterProxy
在它内部创建。我不想创造