![](/img/trans.png)
[英]What is the purpose of adding a ContextLoaderListener to the ServletContext?
[英]Organizing beans in ServletContext and ContextLoaderListener
我有一個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
則可以在Controllers中使用我的服務,但是不能在application-context.xml
創建的其他服務中使用它。
我的問題是如何在這種應用程序配置中組織Bean創建。 我覺得這里缺少一些簡單的東西。
編輯:使用特定配置文件啟動應用程序時,會將過濾器添加到過濾器鏈,從而可以模擬API調用。 該過濾器是在應用程序上下文中定義的,因為這是定義彈簧安全性的地方。 它還需要訪問服務,該服務可以提供所有請求映射的列表以及與之關聯的我們的自定義權限(不,它們不能很好地轉換為spring角色)。 但是,此服務需要在Servlet上下文中定義,因為它需要訪問RequestMappingHandlerMapping
。
編輯#2:我公司生產的MWE(可能768,16是最小的不工作實例?)這里: https://github.com/guilty/separate-spring-contexts 。
現在,有一個ExampleController
和CoreService
。 它們都是在單獨的上下文中創建的,需要訪問MappedUrlsService
,而后者又希望可訪問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
示例。 請注意,運行它需要Java 7
和Maven
。
RequestMappingHandlerMapping設計為僅在控制器層中使用(通常與<mvc:annotation-driven/>
),因此應僅在servlet-context.xml中使用
您是否有理由在服務層中需要此服務?
在DispatcherServlet
的上下文中創建一個RequestMappingHandlerMapping
,它可以訪問在根上下文(application-context.xml)中定義的bean,但不能進行其他操作。 但是,您可以在根上下文中定義一個RequestMappingHandlerMapping
bean。 Spring MVC應該可以毫無問題地進行處理。 但這在servlet上下文之外確實沒有任何意義。
好吧,我不會討論您的bean組織的原因,而只會討論如何。
您可以從servlet上下文的bean中注入來自根上下文的bean,但不能相反,因為servlet上下文是作為根上下文作為其父級創建的。
恕我直言,在服務層中依賴於控制器層中的bean確實是一個糟糕的設計,在現實世界中,您應盡量避免使用它。 但讓我們繼續。
我建議您在控制器程序包和servlet上下文中創建一個中繼bean,並將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" />
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.