问题
I was looking at this https://www.baeldung.com/configuration-properties-in-spring-boot and was wondering if it was possible to use constructor injection for these in order to enforce some immutability properties.
For example would it be possible to do this:
@Component
@ConfigurationProperties("my-config")
public class MyConfig {
    private final List<String> values;
    public MyConfig(@Value("${values}") List<String> values) {
        this.values = ImmutableList.copyOf(values);
    }
}
And then in my yml config have
my-config.values:
  - foo
  - bar
But I get this error:
java.lang.IllegalArgumentException: Could not resolve placeholder 'values' in string value "${values}"
    回答1:
The documentation states :
Property values can be injected directly into your beans by using the @Value annotation, accessed through Spring’s Environment abstraction, or be bound to structured objects through @ConfigurationProperties. :
You actually try to mix their behavior.values is not a property of the Spring environment but my-config.values is.
Even declared inside MyConfig such as @Value("${values})" it doesn't change anything as  @ConfigurationProperties  bounds the properties to a structured object. And of course it doesn't create new properties in the Spring environment, that is where @Value() looks for to resolve the value expression.
Whereas the exception to resolve ${values}.
As MyConfig is a component @Value should be what you need :
@Component
public class MyConfig {
    private final List<String> values;
    public MyConfig(@Value("${my-config.values}") List<String> values) {
        this.values = ImmutableList.copyOf(values);
    }
}
You could also prevent the mutability by protecting the setter with a check but this will detect the issue only at runtime :
@ConfigurationProperties("my-config")
public class MyConfig {
    private final List<String> values;
    public List<String> getValue(){
         return values;
    }
    public void setValue(List<String> values){  
         if (this.values != null){
             throw new IllegalArgumentException("...");
         }                    
         this.values = ImmutableList.copyOf(values);
    }
}
    回答2:
is possible with spring boot since version 2.2.0 documentation is here: Constructor binding adding the new annotation @ConstructorBinding.
回答3:
For @ConfigurationProperties spring uses properties (or setter) injection only. So this class should be mutable. 
Also, it is not necessary to have a constructor in this case (and @Value annotation can be skipped as well), and class can be as simple as this:
@Component
@ConfigurationProperties("my-config")
public class MyConfig {
   private List<String> values;
   //getter+setter
}
    回答4:
Remove @Value("${values}") and just use getters / setters
Or use SpEL - @Value("#{'${my-second-config.values}'.split(',')}"   
Also, take look at Spring boot documentation
Example below:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
}
@Component
@ConfigurationProperties("my-config")
public static class MyConfig {
    private List<String> values;
    public List<String> getValues() {
        return values;
    }
    public void setValues(List<String> values) {
        this.values = ImmutableList.copyOf(values);
    }
}
@Configuration
public static class MySecondConfig {
    private final List<String> values;
    @Autowired
    public MySecondConfig(@Value("#{'${my-second-config.values}'.split(',')}") 
                                 List<String> values) {
        this.values = ImmutableList.copyOf(values);
    }
    public List<String> getValues() {
        return values;
    }
}
@Service
public static class MyService {
    private final MyConfig myConfig;
    private final MySecondConfig mySecondConfig;
    @Autowired
    public MyService(MyConfig myConfig, MySecondConfig mySecondConfig) {
        this.myConfig = myConfig;
        this.mySecondConfig = mySecondConfig;
    }
    @PostConstruct
    public void startUp() {
        myConfig.getValues().forEach(System.out::println);
        mySecondConfig.getValues().forEach(System.out::println);
    }
}
}
application.properties :
my-config.values[0]=a
my-config.values[1]=b
my-second-config.values=c,d
console output:abcd  
回答5:
@ConfigurationProperties binds the values with the given prefix. @ConfigurationProperties and @Value are not related. So @value will not be a relative path to the path you mentioned with @ConfigurationProperties. Also @ConfigurationProperties uses setters to inject the values, if you wish to make properties immutable you'll have to tweak your setters in some way.
public void setProperty(String property){
if(this.property == null){
this.property = property;
}
}
    来源:https://stackoverflow.com/questions/51790207/use-constructor-injection-for-spring-configurationproperties-subclasses