我现在有一些Spring的经验,也有一些纯java配置的web应用程序在使用中。然而,这些应用程序通常都是基于一个安静简单的设置:
- 服务/存储库的应用程序配置
- 一个调度程序(和一些控制器)的调度程序配置
- (可选)Spring安全装置,用于保护访问
对于我当前的项目,我需要有不同配置的单独的调度器上下文。对于基于XML的配置,这不是问题,因为我们有一个独立于调度器配置的专用ContextLoaderListener。但是对于java配置,我不确定到目前为止我所做的是否正确;)
下面是一个常见的DispatcherConfig:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new class[]{MyAppConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MyDispatcherConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/mymapping/*"};
}
@Override
protected String getServletName() {
return "myservlet";
}
}
如前所述,我需要第二个(第三个,......)调度器,它有另一个Map(和视图解析器)。因此,我复制了配置并添加了两个getServletName()(否则两个都将被命名为'dispatcher',这将导致错误)。第二个配置如下所示:
public class AnotherWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new class[]{MyAppConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{AnotherDispatcherConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/another_mapping/*"};
}
@Override
protected String getServletName() {
return "anotherservlet";
}
}
当我像这样使用它时,启动应用程序会导致ContextLoaderListener出现问题:
java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:277)
...
所以我从一个 AbstractAnnotationConfigDispatcherServletInitializer 中删除了第二个MyAppConfig.class返回,它工作正常。)
对于我的理解:所有的DispatcherConfig应该在一个 AbstractAnnotationConfigDispatcherServletInitializer 中处理还是应该像我做的那样将它们分开?我尝试在一个类中配置它们,但是我的配置完全是混合的(所以我认为这不是理想的方式)。
如何实现这种情况?是否可以在 AbstractAnnotationConfigDispatcherServletInitializer 之外的java配置中设置 ContextLoaderListener?或者我应该创建一个只有根配置的 DefaultServlet?如何实现该配置的基本接口 WebApplicationInitializer?
4条答案
按热度按时间5kgi1eie1#
Mahesh C.展示了正确的道路,但他的实施太有限了。他在一点上是正确的:您不能直接将
AbstractAnnotationConfigDispatcherServletInitializer
用于多个分派器servlet。但是实现应该:下面是一个更完整的实现:
这样,您就可以完全控制哪些bean将在哪个上下文中结束,就像您对XML配置所做的那样。
ubbxdtey2#
我认为,如果您使用通用WebApplicationInitializer接口,而不是使用Spring提供的抽象实现- AbstractAnnotationConfigDispatcherServletInitializer,您可以解决这个问题。
这样,您就可以创建两个单独的初始化程序,这样您就可以在startUp()方法上获得不同的ServletContext,并为每个初始化程序注册不同的AppConfig & dispatcher servlet。
其中一个实现类可能如下所示:
8gsdolmq3#
我也遇到了同样的问题。实际上,我有一个复杂的配置,有多个调度器servlet、过滤器和监听器。
我有一个如下所示的web.xml
我用下面的java文件替换了上面的web.xml
qmelpv7a4#
它可以而且应该使用几个AbstractAnnotationConfigDispatcherServletInitializer类来完成,每个调度程序一个类。@Serge Ballesta的答案在这方面是不正确的。
解决方案是将第二个初始化器的rootConfigClasses设置为null,以防止ContextLoaderListener设置根上下文两次,这是您遇到的错误。当加载第二个DispatcherServlet时,它将查找在servletContext中注册的根上下文,因此两个调度器上下文最终将共享相同的根上下文,而不会出现任何问题。
但你要注意:
getRootConfigClasses
中返回null,以避免ContextLoaderListener两次注册根上下文。此修复程序是问题代码所必需的:
或者,您也可以使用一个
WebApplicationInitializer
手动完成所有操作,如@Serge Ballesta的答案所示。一些附加说明:
ContextListener
不是强制性的,它只是初始化上下文,这可以通过在上下文上调用refresh
方法来完成。WebApplicationInitializer
类,则每个调度程序可以有不同的类,使用@Order
注解排序。RequestMappingHandlerMapping
,将完整的URL传递给那些没有默认Map(“/”)的控制器,否则默认情况下它会修剪调度程序Map部分。这将简化您的测试。Spring-boot会自动执行此操作,或者如果您不使用它,也可以使用WebMvcConfigurer
:registerContextLoaderListener
方法并在其他初始化器中手动注册它。尽管通常值得让第一个初始化器来做这件事。但是这可能是有用的,例如,如果你有两个具有不同父上下文的调度器,并且需要避免将它们都注册为根上下文。** Spring 安全**
当有多个调度器时,一个重要的问题是Spring Security配置。这可以通过向上下文添加一个扩展AbstractSecurityWebApplicationInitializer的类来完成。它在Map到“/*"的调度器配置之后注册一个名为
DelegatingFilterProxy
的过滤器。默认情况下,该过滤器在根上下文中查找securityFilterChain
bean。当使用@EnableWebSecurity
注解时,此bean被添加到上下文中,该注解通常位于根上下文中,因此您可以在不同的调度程序之间共享安全配置。但是,您也可以将安全配置放在一个调度程序上下文中,并告诉过滤器使用init-参数contextAttribute
。您可以使用bean
WebSecurityCustomizer
和SecurityFilterChain
(来自Spring Security 5.7)或扩展以前的WebSecurityConfigurer
类来共享安全配置。或者,您可以为每个调度程序使用不同的bean,配置多个 web 和 http 元素。或者,您甚至可以通过为每个调度程序注册一个过滤器来为不同的调度程序配置不同的配置。过滤器必须有不同的名称,并且名称是硬编码在
AbstractSecurityWebApplicationInitializer
类中的(最高为spring security 5.7)。因此,您可以创建:AbstractSecurityWebApplicationInitializer
。其他参考: