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

Spring Flow与JSF集成时HttpRequestHandlerAdapter Bean冲突问题

问题背景

我正在构建基于Spring Flow与JSF的项目,使用Gradle作为构建工具,build.gradle配置如下:

plugins {
    id 'java'
    id 'war'
    id 'org.springframework.boot' version '3.1.2'
    id 'io.spring.dependency-management' version '1.1.2'
}

group = 'test.pkg'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.joinfaces:mojarra-spring-boot-starter:5.1.2'
    compileOnly 'org.springframework.webflow:spring-webflow:3.0.0'
    implementation 'org.springframework.webflow:spring-webflow:3.0.0'
    implementation 'org.springframework.webflow:spring-faces:3.0.0'
    implementation 'jakarta.faces:jakarta.faces-api:4.0.1'

    runtimeOnly 'jakarta.faces:jakarta.faces-api:4.0.1'
    runtimeOnly 'org.springframework.webflow:spring-faces:3.0.0'
    runtimeOnly 'org.springframework.webflow:spring-webflow:3.0.0'
    runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

尝试用纯Java方式完成所有配置,编写了如下@Configuration类:

@Configuration
public class WebFlowConfig extends AbstractFacesFlowConfiguration {

    @Bean
    public FlowDefinitionRegistry flowDefinitionRegistry(FlowBuilderServices flowBuilderServices) {
        return getFlowDefinitionRegistryBuilder()
                .setBasePath("/WEB-INF")
                .addFlowLocation("/flows/**/*.xml")
                .setFlowBuilderServices(flowBuilderServices) // 11.5 view resolution
                .build();
    }

    @Bean
    public FlowExecutor flowExecutor(FlowDefinitionRegistry flowDefinitionRegistry) {
        return getFlowExecutorBuilder(flowDefinitionRegistry)
                .addFlowExecutionListener(new FlowFacesContextLifecycleListener())  // 13.3
                .build();
    }

    @Bean
    public FlowBuilderServices flowBuilderServices(MvcViewFactoryCreator mvcViewFactoryCreator) {
        return getFlowBuilderServicesBuilder()
                .setDevelopmentMode(true)
                .setViewFactoryCreator(mvcViewFactoryCreator)
                .build();
    }

    @Bean
    public MvcViewFactoryCreator mvcViewFactoryCreator() {
        MvcViewFactoryCreator mvcViewFactoryCreator = new MvcViewFactoryCreator();
        return mvcViewFactoryCreator;
    }

    //13.2
    @Bean
    public ServletRegistrationBean jsfServletBean() {
        ServletRegistrationBean<jakarta.faces.webapp.FacesServlet> servletRegistrationBean = new ServletRegistrationBean(new FacesServlet(), "*.xhtml");
        servletRegistrationBean.setLoadOnStartup(1);
        return servletRegistrationBean;
    }

    //auch 13.2
    @Bean
    public ServletContextInitializer initializer() {
        return new ServletContextInitializer() {
            @Override
            public void onStartup(ServletContext servletContext) throws ServletException {
                servletContext.setInitParameter("javax.faces.DEFAULT_SUFFIX", ".xhtml");
            }
        };
    }

    //13.3
    public JsfFlowHandlerAdapter jsfFlowHandlerAdapter(FlowExecutor flowExecutor) {
        JsfFlowHandlerAdapter jsfFlowHandlerAdapter = new JsfFlowHandlerAdapter();
        jsfFlowHandlerAdapter.setFlowExecutor(flowExecutor);
        return jsfFlowHandlerAdapter;
    }

    @Bean
    public ServletRegistrationBean webFlowServletBean() {
        ServletRegistrationBean<DispatcherServlet> bean = new ServletRegistrationBean(new DispatcherServlet(), "/flows/*");
        bean.addInitParameter("contextConfigLocation", "/WEB-INF/web-application-config.xml");
        bean.setLoadOnStartup(1);
        bean.setOrder(1);
        return bean;
    }

    @Bean
    public FlowHandlerMapping flowHandlerMapping(FlowDefinitionRegistry flowDefinitionRegistry) {
        FlowHandlerMapping flowHandlerMapping = new FlowHandlerMapping();
        flowHandlerMapping.setFlowRegistry(flowDefinitionRegistry);
        flowHandlerMapping.setOrder(0);
        return flowHandlerMapping;
    }
}

运行项目时出现以下错误:

The bean 'httpRequestHandlerAdapter', defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [test/pkg/testflow/config/WebFlowConfig.class] and overriding is disabled.

我不想开启Bean覆盖,希望使用自定义Bean,求纯Java配置的Spring Flow+JSF项目解决方案。


问题分析与解决方案

1. 冲突根源

AbstractFacesFlowConfiguration已自动注册httpRequestHandlerAdapter Bean,而Spring Boot的WebMvc自动配置也会注册同名Bean,导致冲突。此外配置存在冗余和错误:

  • 重复依赖:同时声明compileOnly和implementation的spring-webflow,属于冗余
  • 手动注册FacesServlet:JoinFaces Starter已自动配置FacesServlet,无需手动注册
  • 多余的DispatcherServlet注册:Spring Boot已有默认DispatcherServlet,无需单独为WebFlow注册新实例
  • JsfFlowHandlerAdapter未加@Bean注解,无法被Spring容器识别

2. 优化后的Gradle依赖

精简build.gradle,移除冗余依赖:

plugins {
    id 'java'
    id 'war'
    id 'org.springframework.boot' version '3.1.2'
    id 'io.spring.dependency-management' version '1.1.2'
}

group = 'test.pkg'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    // Spring Boot Web基础
    implementation 'org.springframework.boot:spring-boot-starter-web'
    // JoinFaces整合JSF(自动配置FacesServlet等)
    implementation 'org.joinfaces:mojarra-spring-boot-starter:5.1.2'
    // Spring WebFlow核心依赖
    implementation 'org.springframework.webflow:spring-webflow:3.0.0'
    implementation 'org.springframework.webflow:spring-faces:3.0.0'
    // 数据库驱动
    runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
    // 测试依赖
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

3. 修正后的WebFlow配置类

移除冗余配置,解决Bean冲突,确保JsfFlowHandlerAdapter被正确注册:

@Configuration
public class WebFlowConfig extends AbstractFacesFlowConfiguration {

    // 定义Flow注册表,扫描WEB-INF下的flow配置文件
    @Bean
    public FlowDefinitionRegistry flowDefinitionRegistry(FlowBuilderServices flowBuilderServices) {
        return getFlowDefinitionRegistryBuilder()
                .setBasePath("/WEB-INF")
                .addFlowLocationPattern("/flows/**/*.xml")
                .setFlowBuilderServices(flowBuilderServices)
                .build();
    }

    // 定义Flow执行器,添加JSF生命周期监听器
    @Bean
    public FlowExecutor flowExecutor(FlowDefinitionRegistry flowDefinitionRegistry) {
        return getFlowExecutorBuilder(flowDefinitionRegistry)
                .addFlowExecutionListener(new FlowFacesContextLifecycleListener())
                .build();
    }

    // 配置Flow构建服务,启用开发模式,关联视图工厂
    @Bean
    public FlowBuilderServices flowBuilderServices(MvcViewFactoryCreator mvcViewFactoryCreator) {
        return getFlowBuilderServicesBuilder()
                .setDevelopmentMode(true)
                .setViewFactoryCreator(mvcViewFactoryCreator)
                .build();
    }

    // MVC视图工厂创建器,适配JSF视图解析
    @Bean
    public MvcViewFactoryCreator mvcViewFactoryCreator() {
        MvcViewFactoryCreator factoryCreator = new MvcViewFactoryCreator();
        // 设置JSF视图后缀,与JoinFaces保持一致
        factoryCreator.setViewSuffix(".xhtml");
        return factoryCreator;
    }

    // 注册JSF Flow处理器适配器,必须加@Bean注解
    @Bean
    public JsfFlowHandlerAdapter jsfFlowHandlerAdapter(FlowExecutor flowExecutor) {
        JsfFlowHandlerAdapter adapter = new JsfFlowHandlerAdapter();
        adapter.setFlowExecutor(flowExecutor);
        return adapter;
    }

    // Flow处理器映射,设置优先级高于默认MVC映射
    @Bean
    public FlowHandlerMapping flowHandlerMapping(FlowDefinitionRegistry flowDefinitionRegistry) {
        FlowHandlerMapping mapping = new FlowHandlerMapping();
        mapping.setFlowRegistry(flowDefinitionRegistry);
        mapping.setOrder(0);
        return mapping;
    }

    // 配置JSF初始化参数,替代手动ServletContextInitializer
    @Bean
    public ServletContextInitializer jsfInitializer() {
        return servletContext -> {
            servletContext.setInitParameter("jakarta.faces.DEFAULT_SUFFIX", ".xhtml");
            // 可添加其他JSF配置参数,比如开发模式
            servletContext.setInitParameter("jakarta.faces.DEVELOPMENT", "true");
        };
    }
}

4. 关键说明

  • 移除手动注册的FacesServlet和额外的DispatcherServlet,由JoinFaces和Spring Boot自动配置处理,避免冲突
  • 修正JsfFlowHandlerAdapter的@Bean注解,确保Spring能识别并管理该Bean
  • 使用addFlowLocationPattern替代addFlowLocation,支持批量扫描Flow配置文件
  • 替换javax.faces前缀为jakarta.faces,符合Jakarta EE 9+规范(Spring Boot 3.x基于Jakarta EE)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 16:45:54