Spring Boot 找不到 @ConfigurationProperties 配置类 Bean
配置键存在却报配置类 Bean 找不到时,区分属性绑定与 Bean 注册。按显式启用、属性扫描或 JavaBean 组件注册处理,核对包范围及 record 构造器绑定。
application.yml 里有配置,属性类上也写了 @ConfigurationProperties,应用却在构造器注入时退出:
required a bean of type 'com.example.config.FileProperties' that could not be found
这条错误指向容器里没有可注入的 FileProperties。还没到检查 upload-dir 是否绑定成功的阶段。@ConfigurationProperties 声明属性前缀和绑定规则,本身不会把一个普通类注册为 Bean。
下面以 Spring Boot 3.x、Java 17 的 JavaBean 属性类为例。@ConfigurationPropertiesScan 从 Boot 2.2 起提供;旧版本不能直接套用扫描写法。
给属性类一个明确的注册入口
假设配置为:
app:
file:
upload-dir: /srv/example/uploads
属性类保留无参构造器、getter 和 setter:
package com.example.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app.file")
public class FileProperties {
private String uploadDir;
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
}
项目只有少量配置类时,在已加载的配置类或启动类上逐个启用,注册范围比较清楚:
package com.example;
import com.example.config.FileProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(FileProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这里不能省略 FileProperties.class。空的 @EnableConfigurationProperties 会启用相关支持,却没有列出这个类,也不会扫描所有属性类。
如果项目按包集中管理配置类,可以将上面的启用注解换成:
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan(basePackageClasses = FileProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这段是启动类的替换示例,其余 import 与前一段相同。basePackageClasses 扫描指定类所在的包;省略它时,从声明扫描注解的类所在包开始。启动类在 com.example.app、属性类在 com.example.config 时,两者是兄弟包,默认范围不会覆盖属性类。
已经使用组件扫描的 JavaBean 属性类也可以加 @Component,前提是它位于组件扫描范围内。三种入口择一即可,不需要把扫描、显式启用和组件注解全部叠上去。
Bean 已存在,但属性还是空值
注册修正后,如果构造器能接收到 FileProperties,而 getUploadDir() 返回 null,问题已经变了。此时核对前缀 app.file、实际激活的 profile、最终生效的配置值,以及 JavaBean 的 setter。调整扫描范围不能补上拼错的配置键。
upload-dir 能绑定到 uploadDir,不需要为这种连字符写法额外加 @Value。示例中的目录仅作为配置值展示,注册属性 Bean 本身不会创建目录,也不会检查文件系统权限。
如果注册注解写在一个没有被应用加载的 @Configuration 类上,它同样不会起作用。可在本机调试当前 ApplicationContext 中是否存在该类型的 Bean,先确认配置类是否被扫描或导入,再追属性值。不要用 new FileProperties() 绕过注入:自己创建的对象不会因为类上有绑定注解就自动收到外部配置。
换成 record 后别沿用 @Component
Boot 3.x 可以用 record 承接构造器绑定:
package com.example.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app.file")
public record FileProperties(String uploadDir) {
}
它仍通过显式启用或属性扫描注册。不要为了让它成为 Bean 再加 @Component;普通组件创建会走依赖注入,可能转而寻找 String 类型的 Bean。普通 Java 配置类做构造器绑定时,实际编译任务应开启 -parameters。Java 17 的 javac 会为 record 的规范构造器保留参数名,因此不能把这个 record 能否绑定简单归结为有没有该选项;项目统一开启它,仍有助于其他依赖参数名的 Java 代码。
对于 JavaBean 版本,修改后的判断标准是:原来的缺失 Bean 错误消失,业务组件能取得属性对象,并且 getUploadDir() 等于当前环境配置的值。对于 record 版本,则读取 uploadDir()。若测试中能取得对象、正式启动仍失败,应比较两边加载的配置类和 profile,不要把 IDE 的提示当成运行时已经注册的证明。


