我有一个spring-webmvc + spring-security应用程序,我遇到了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 security。它还需要访问一个服务,该服务可以提供所有请求映射的列表以及与它们相关的自定义权限(不,它们不能很好地转换为spring角色)。但是,需要在servlet上下文中定义此服务,因为它需要访问RequestMappingHandlerMapping
。
编辑#2 :我在这里制作了一个MWE(可能是最小的不工作示例?):https://github.com/guilty/separate-spring-contexts。
现在,有一个ExampleController
和CoreService
。它们都是在不同的上下文中创建的,需要访问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}}。
答案 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" />