Spring @Value注入无法读取properties属性,@ConfigurationProperties正常,求原因
Let's break down why your @Value injection is failing even though @ConfigurationProperties works, and how to fix it.
The Problem Recap
You have this component using constructor injection with @Value:
@Component public class FaultsConfiguration { private int interval; @Autowired public FaultsConfiguration(@Value("${faults.interval}") int interval) { this.interval = interval; } }
With application.properties containing:
faults.interval=130
This throws a IllegalArgumentException: Could not resolve placeholder 'faults.interval', but switching to @ConfigurationProperties works fine:
@Service @ConfigurationProperties(prefix="faults") @Data @NoArgsConstructor public class FaultsConfiguration { private int interval; }
Root Cause: Initialization Order & Injection Timing
The key difference lies in when Spring resolves the properties for each approach:
- Constructor injection with
@Value: Spring needs to resolve the placeholder${faults.interval}before creating the bean instance (since it's required for the constructor). If the property sources (likeapplication.properties) haven't been fully loaded into the SpringEnvironmentyet, or the placeholder resolver (PropertySourcesPlaceholderConfigurer) isn't initialized, this fails. @ConfigurationProperties: This uses Spring's Binder API, which resolves properties after the bean is instantiated (via setters, which Lombok's@Datagenerates). By this point, theEnvironmentis fully populated with all property sources, so the value is available.
Other possible contributing factors:
- If you have a custom
PropertyPlaceholderConfigurer(the older Spring framework class) in your project, it might override Spring Boot's defaultPropertySourcesPlaceholderConfigurer, which doesn't support the modern property source hierarchy that includesapplication.properties. - Constructor injection with
@Valueis more sensitive to bean initialization order than field/setter injection.
Fixes to Get @Value Working
Here are a few ways to resolve the issue:
1. Switch to Field or Setter Injection with @Value
Move the @Value annotation to a field or setter method, so property resolution happens after bean instantiation:
@Component public class FaultsConfiguration { @Value("${faults.interval}") private int interval; // No-arg constructor (required for Spring to instantiate the bean first) public FaultsConfiguration() { } }
Or with setter injection:
@Component public class FaultsConfiguration { private int interval; @Autowired public void setInterval(@Value("${faults.interval}") int interval) { this.interval = interval; } }
2. Ensure PropertySourcesPlaceholderConfigurer is Properly Configured
If you must use constructor injection, make sure Spring's PropertySourcesPlaceholderConfigurer is initialized before your FaultsConfiguration bean. In Spring Boot, this is usually auto-configured, but if you have custom property configs, explicitly define it:
@Configuration public class PropertyConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } }
The static modifier ensures this bean is created early in the initialization process.
3. Replace Old PropertyPlaceholderConfigurer with PropertySourcesPlaceholderConfigurer
If you have any custom beans using the legacy PropertyPlaceholderConfigurer, replace them with PropertySourcesPlaceholderConfigurer—this class is designed to work with Spring Boot's environment and property sources.
内容的提问来源于stack exchange,提问作者J Person

