Spring多Profile启动报NoSuchBeanDefinitionException异常求助
Spring两类NoSuchBeanDefinitionException异常排查修复
已复现的异常与配置基线
- 测试类
SpringMain运行抛出异常:org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available。测试代码已通过setActiveProfiles激活postgres、datajpa两个Profile,加载配置文件为spring/inmemory.xml - Tomcat启动抛出异常:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'topjava.quest.service.RestaurantService' available: expected at least 1 bean which qualifies as autowire candidate。RestaurantService类已标注@Service注解,注入位置配置@Autowired(required=true),全局无重复Bean定义 - 现有配置情况:
- web.xml配置默认激活Profile为
postgres,datajpa - 根上下文加载配置路径:
classpath:spring/spring-app.xml、classpath:spring/spring-db.xml - SpringMVC子上下文加载配置路径:
spring-mvc.xml spring-db.xml内postgresProfile下定义dataSource相关Bean,datajpaProfile下定义entityManagerFactory、JPA仓库配置、datajpa包组件扫描规则- pom.xml内Spring、JPA、Hibernate、数据库驱动依赖均配置完整
- web.xml配置默认激活Profile为
根因定位与修复操作
1. 测试场景下entityManagerFactory缺失修复
核心问题:配置文件加载范围与Profile激活时机错误entityManagerFactory的Bean定义存放在spring-db.xml的datajpaProfile节点下,但测试上下文仅加载了spring/inmemory.xml,未导入spring-db.xml配置,即使正确激活Profile,Spring也读不到对应Bean定义,必然抛出找不到Bean的异常。
按以下顺序修复:
- 在
spring/inmemory.xml中添加配置导入,确保JPA相关Bean定义能被测试上下文加载:
<import resource="classpath:spring/spring-db.xml"/>
- 校验
setActiveProfiles调用时机:必须在Spring上下文执行refresh操作之前调用,否则Profile激活不生效,正确写法参考:
// 错误示例:先刷新上下文再设置Profile,配置完全不生效 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.refresh(); ctx.getEnvironment().setActiveProfiles("postgres", "datajpa"); // 正确示例 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("postgres", "datajpa"); ctx.load("spring/inmemory.xml"); ctx.refresh();
- 校验
spring-db.xml内Profile标签拼写:确认datajpa、postgres对应的<beans profile="xxx">标签无大小写错误、分隔符错误(比如误写为dataJpa、data-jpa)。
2. Tomcat启动时RestaurantService缺失修复
核心问题:Profile配置格式错误+父子容器扫描范围不匹配
90%以上的SpringMVC场景下Service Bean缺失,都逃不开以下两个配置错误:
错误点1:web.xml多Profile配置分隔符错误
spring.profiles.default参数配置多个Profile时,必须用空格分隔,不能用逗号。逗号分隔会被Spring识别为单个名称为postgres,datajpa的Profile,完全匹配不到你定义的两个独立Profile,对应Profile下的所有Bean(数据源、entityManagerFactory、Service扫描规则)都不会被创建。
修正配置如下:
<!-- 错误写法:逗号分隔导致Profile识别失败 <context-param> <param-name>spring.profiles.default</param-name> <param-value>postgres,datajpa</param-value> </context-param> --> <!-- 正确写法:空格分隔多Profile --> <context-param> <param-name>spring.profiles.default</param-name> <param-value>postgres datajpa</param-value> </context-param>
错误点2:父子容器组件扫描范围配置错误
SpringMVC为父子上下文结构:根上下文负责加载Service、DAO、数据源等通用Bean,MVC子上下文仅负责加载Controller、视图解析器等Web层Bean。扫描范围配置错误会导致Service类根本没被注册到Spring容器中:
- 根上下文(
spring-app.xml)扫描配置需覆盖Service、DAO层,排除Controller注解:
<context:component-scan base-package="topjava.quest.service, topjava.quest.repository"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
- MVC子上下文(
spring-mvc.xml)仅扫描Controller层,不要重复扫描Service/DAO,避免上下文隔离导致注入失败:
<context:component-scan base-package="topjava.quest.web" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
验证顺序
- 先运行测试类
SpringMain,确认entityManagerFactory正常创建,测试用例可正常连接数据库 - 启动Tomcat,查看启动日志打印的激活Profile列表,确认
postgres、datajpa均被正确激活,无Profile相关警告 - 发起接口请求,验证
RestaurantService注入正常,业务逻辑可正常执行
内容的提问来源于stack exchange,提问作者VadiMayer
相关产品推荐
相关产品推荐

