Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use constructor injection for spring ConfigurationProperties subclasses

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}"
like image 608
0x26res Avatar asked Aug 10 '18 15:08

0x26res


People also ask

How do I inject my ConfigurationProperties?

To work with @ConfigurationProperties beans, you can inject them in the same way as any other bean, as shown in the following example: @Service public class MyService { private final AcmeProperties properties; @Autowired public MyService(AcmeProperties properties) { this. properties = properties; } //...

Does Spring support constructor injection?

As of Spring 4.3, classes with a single constructor can omit the @Autowired annotation. This is a nice little bit of convenience and boilerplate removal. On top of that, also starting with 4.3, we can leverage the constructor-based injection in @Configuration annotated classes.

What is the limitation of constructor injection in Spring?

Constructor injection (from the definition) does not allow you to create circular dependencies between beans. This limitation is actually an advantage of constructor injection - Spring can resolve circular dependencies when setter injection is used without you even noticing.

Which is better setter or constructor injection in Spring?

If we use both constructor and setter injection, IOC container will use the setter injection. Changes: We can easily change the value by setter injection. It doesn't create a new bean instance always like constructor. So setter injection is flexible than constructor injection.


2 Answers

is possible with spring boot since version 2.2.0 documentation is here: Constructor binding adding the new annotation @ConstructorBinding.

like image 105
nekperu15739 Avatar answered Sep 27 '22 23:09

nekperu15739


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);
    }
}
like image 23
davidxxx Avatar answered Sep 28 '22 00:09

davidxxx