容器内跨部署单元共享Spring上下文的更优实现方案咨询
Spring Boot EAR-WAR 父上下文优化方案
问题背景
我们正在将一款EJB应用迁移至Spring框架,当前在WildFly 9.0.2容器中部署了包含单体Spring Boot应用的Platform.ear,以及两个扩展其功能的Spring Boot WAR包(Processor.WAR、UI.WAR)。最初每个WAR会重复初始化EAR中的所有Bean,后来通过ApplicationContext.setParent将EAR的应用上下文设为WAR的父上下文,实现了EAR Bean仅初始化一次,且共享库不在WAR扫描路径中。目前通过自定义Bean注册表实现该逻辑,但希望找到更优的基于ServletContext的实现方式。
当前实现
WAR中通过自定义注册表设置父上下文的核心代码:
public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //... @Override protected WebApplicationContext createServletApplicationContext() { final WebApplicationContext servletApplicationContext = super.createServletApplicationContext(); SpringContextConfigurer.setPlatformAsParent(servletApplicationContext); // 从自定义注册表查找EAR上下文并设为父 return servletApplicationContext; } //... }
曾尝试通过ServletContext直接获取,但无法定位平台应用上下文:
@Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext.setAttribute("org.springframework.web.context.WebApplicationContext.ROOT", servletContext.getContext("/platform").getAttribute("org.springframework.web.context.WebApplicationContext.ROOT")); super.onStartup(servletContext); }
优化方案建议
1. 基于ServletContext跨上下文获取父上下文(直接可行)
要实现WAR通过ServletContext访问EAR的上下文,需完成两个关键配置:
- 开启跨上下文访问:在EAR中Web模块的
WEB-INF/jboss-web.xml中添加crossContext="true",允许WAR访问其他应用的上下文:<jboss-web> <context-root>/platform</context-root> <cross-context>true</cross-context> </jboss-web> - 调整WAR的onStartup逻辑:利用Spring内置常量
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE获取EAR的根上下文,确保在创建自身上下文前设置父上下文:@Override public void onStartup(ServletContext servletContext) throws ServletException { // 获取EAR对应的ServletContext(需匹配EAR Web模块的context-root) ServletContext platformServletContext = servletContext.getContext("/platform"); if (platformServletContext != null) { // 获取EAR的根WebApplicationContext WebApplicationContext parentContext = (WebApplicationContext) platformServletContext.getAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE ); if (parentContext != null) { // 将EAR上下文设为当前WAR的父上下文 servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, parentContext ); } } // 调用父类方法初始化自身上下文 super.onStartup(servletContext); } - 确保EAR优先启动:在WildFly的
standalone.xml中配置部署顺序,或在EAR的application.xml中明确模块顺序,避免WAR启动时EAR上下文未初始化:<application xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="7"> <module> <ejb>Platform-ejb.jar</ejb> </module> <module> <web> <web-uri>Platform-web.war</web-uri> <context-root>/platform</context-root> </web> </module> </application>
2. Spring Boot配置类绑定父上下文(更优雅)
在WAR的配置类中直接注入ServletContext,获取并设置父上下文,避免在WebApplicationInitializer中硬编码:
@Configuration @ComponentScan(basePackages = "com.war", excludeFilters = { @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.platform.*") }) public class WarAppConfig { @Autowired private ServletContext servletContext; @Bean public ApplicationContext parentApplicationContext() { ServletContext platformContext = servletContext.getContext("/platform"); return (ApplicationContext) platformContext.getAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE ); } @PostConstruct public void setupParentContext() { ConfigurableApplicationContext currentContext = (ConfigurableApplicationContext) servletContext.getAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE ); currentContext.setParent(parentApplicationContext()); } }
注:@ComponentScan的excludeFilters需排除EAR的包路径,避免重复扫描Bean。
3. WildFly类加载优化(避免冲突)
通过jboss-deployment-structure.xml将EAR的共享库配置为全局模块,确保WAR直接引用而不重复打包,同时避免类加载冲突:
<jboss-deployment-structure> <deployment> <dependencies> <module name="com.platform.shared" export="true"/> </dependencies> </deployment> <sub-deployment name="Processor.war"> <dependencies> <module name="com.platform.shared"/> </dependencies> </sub-deployment> <sub-deployment name="UI.war"> <dependencies> <module name="com.platform.shared"/> </dependencies> </sub-deployment> </jboss-deployment-structure>
关键注意事项
- 确保EAR和WAR使用的Spring Boot/Spring版本完全一致(当前为2.4.5/5.3.6),避免类加载兼容性问题;
- 父上下文的Bean应为单例且无状态,避免多WAR共享时出现线程安全问题;
- 若EAR包含EJB模块,需确保Spring能正确识别并集成EJB Bean(可通过
@EJB或Spring的EJB集成类实现)。
内容的提问来源于stack exchange,提问作者Tristan
相关产品推荐
相关产品推荐

