在ServletContext和ContextLoaderListener中组织bean

时间:2014-07-28 12:16:10

标签: spring spring-mvc

我有一个spring-webmvc + spring-security应用程序,我遇到了bean注入问题。首先,org.springframework.web.servlet.DispatcherServletorg.springframework.web.filter.DelegatingFilterProxyorg.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 security。它还需要访问一个服务,该服务可以提供所有请求映射的列表以及与它们相关的自定义权限(不,它们不能很好地转换为spring角色)。但是,需要在servlet上下文中定义此服务,因为它需要访问RequestMappingHandlerMapping

编辑#2 :我在这里制作了一个MWE(可能是最小的工作示例?):https://github.com/guilty/separate-spring-contexts

现在,有一个ExampleControllerCoreService。它们都是在不同的上下文中创建的,需要访问MappedUrlsService,而RequestMappingHandlerMapping又可以访问MappedUrlsService bean。根据您实际创建RequestMappingHandlerMapping bean的位置,您要么找不到MappedUrlsService bean,要么找不到RequestMappingHandlerMapping bean。这里是未找到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

使用Java 7运行示例。请注意,运行它需要Maven和{{1}}。

3 个答案:

答案 0 :(得分:1)

RequestMappingHandlerMapping旨在仅在控制器层中使用(通常使用<mvc:annotation-driven/>),因此应该只在servlet-context.xml中

您的服务层中是否需要此功能?

答案 1 :(得分:0)

RequestMappingHandlerMapping的上下文中创建DispatcherServlet,它可以访问根上下文(application-context.xml)中定义的bean,但不能反过来。但是,您可以在根上下文中定义RequestMappingHandlerMapping bean。 Spring MVC应该选择它没有问题。但是在servlet上下文之外它确实没有意义。

答案 2 :(得分:0)

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

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

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

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

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;
    }
}

这非常难看,因为服务依赖于控制器层类,我反向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" />