You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring 5.x与CDI 2.x集成方案咨询:适配JSF 2.3耦合场景

Spring 5.x + CDI 2.x + JSF 2.3 集成方案实践总结

在这类深度耦合CDI的JSF项目中,我实际踩过不少坑,整理了几个最靠谱的集成方案,你可以根据项目的现有架构和复杂度选择:

1. Apache DeltaSpike Spring 桥接器(最省心的官方社区方案)

这是目前最成熟的双容器桥接方案,DeltaSpike本身就是为了解决Java EE和Spring生态的整合问题而生,专门针对Spring 5.x和CDI 2.x做了适配。

  • 核心原理:通过DeltaSpike的模块自动打通Spring ApplicationContext和CDI BeanManager,让两个容器的Bean可以互相注入。
  • 实操步骤:
    1. 引入DeltaSpike Spring模块的Maven依赖(对应Spring 5.x的版本选delta-spike 1.9+)
    2. 在Spring配置类上添加@EnableDeltaSpikeSpring注解,或者在web.xml中注册DeltaSpike的Spring监听器
    3. 之后你就可以在CDI的JSF Backing Bean中用@Inject注入Spring的Service,也能在Spring的Bean中@Inject CDI的Bean
  • 优势:几乎零自定义代码,社区维护成熟,解决了大部分生命周期和依赖冲突问题
  • 注意点:如果项目用了Spring Boot,需要额外配置DeltaSpike的启动类,避免自动装配冲突

2. 自定义BeanFactory/BeanManager桥接(高度定制化场景)

如果DeltaSpike的默认桥接满足不了你的特殊需求(比如需要过滤特定Bean、自定义生命周期管理),可以自己写代码打通两个容器:

方案A:把CDI Bean注册到Spring容器

写一个Spring的ApplicationContextAware组件,获取CDI的BeanManager,将CDI Bean手动注册到Spring:

@Component
public class CdiToSpringBridge implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanManager cdiBeanManager = CDI.current().getBeanManager();
        // 遍历所有CDI管理的Bean,注册到Spring
        for (Bean<?> cdiBean : cdiBeanManager.getBeans(Object.class).values()) {
            Class<?> beanClass = cdiBean.getBeanClass();
            String beanName = beanClass.getSimpleName();
            if (!applicationContext.containsBean(beanName)) {
                Object beanInstance = cdiBeanManager.getReference(cdiBean, beanClass, null);
                applicationContext.getAutowireCapableBeanFactory()
                    .registerSingleton(beanName, beanInstance);
            }
        }
    }
}

方案B:把Spring Bean暴露为CDI Bean

通过CDI扩展机制,获取Spring的ApplicationContext,将Spring Bean注册为CDI的Bean:

public class SpringToCdiExtension implements Extension {
    public void afterBeanDiscovery(@Observes AfterBeanDiscovery event, BeanManager beanManager) {
        ApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(
            FacesContext.getCurrentInstance().getExternalContext().getContext()
        );
        // 遍历Spring Bean,注册为CDI Bean
        for (String beanName : springContext.getBeanDefinitionNames()) {
            Class<?> beanClass = springContext.getType(beanName);
            if (beanClass != null && !beanClass.isInterface()) {
                event.addBean()
                    .types(beanClass)
                    .createWith(ctx -> springContext.getBean(beanName))
                    .scope(Dependent.class);
            }
        }
    }
}

然后在META-INF/services/javax.enterprise.inject.spi.Extension文件中注册这个扩展类。

3. 统一依赖注入模型(避免双容器复杂度)

如果项目还处于初期或者可以重构,优先选择单容器主导的方案,从根源上避免双容器的冲突:

  • 以Spring为主导:
    1. 把所有JSF Backing Bean改用Spring注解:用@Component代替@Named,@Autowired代替@Inject
    2. 引入Spring Faces的依赖,配置SpringBeanFacesELResolver到faces-config.xml,让JSF可以直接访问Spring Bean
    3. 用Spring的@Scope("view")(需要spring-webmvc-jsf依赖)代替CDI的@ViewScoped
  • 以CDI为主导:
    1. 保留CDI作为JSF的依赖注入容器
    2. 通过Spring的CDI集成模块,把Spring的配置类和Bean暴露为CDI Bean,比如用@Import把Spring配置类引入CDI的Bean中

4. JSF 2.3原生Spring集成(轻量场景)

JSF 2.3本身提供了有限的Spring集成支持,配合Spring Faces可以实现基础的互通:

  • 在faces-config.xml中配置Spring的EL解析器:
<application>
    <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>

这样JSF页面和Backing Bean可以直接访问Spring的Bean,但如果需要CDI和Spring Bean互相注入,还是得配合桥接器。


内容的提问来源于stack exchange,提问作者Helder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 16:42:31