Tony Giaccone
Tony Giaccone

Reputation: 511

Spring Boot and constructor injection

I'm building a new Springboot application, and I'm a bit confused about constructor injection for properties. I'm reading values from a yaml file. And the yaml looks like this:

app:
  name: myApplication
  jdbc:
      jdbcUrl: "jdbc:postgresql://localhost:5432/MyDb"
      driverClassName: org.postgresql.Driver
      maximumPoolSize: 1
      username: domainmanager
      password: password

This is being read into two objects, once which processes the name and the jdbc data is held into a second object.

@Configuration
@ConfigurationProperties(prefix = "app")
public class AppConfig {
    private String name;
    private DataSourceInfo jdbc;

If I use setter based injection everything seems to work perfectly. But I'd really like to use constructor injection but when I add this constructor:

    public AppConfig(    @Value("name") String name,
                         @Value("jdbc") DataSourceInfo dsInfo) {
        this.name = name;
        this.jdbc = dsInfo;
    }

Gives me this error:

Cannot convert value of type 'java.lang.String' to required type 'com.ecomm.properties.DataSourceInfo': no matching editors or conversion strategy found

The DataSourceInfo class is annotated like this:

@Configuration
@ConfigurationProperties(prefix = "jdbc")
public class DataSourceInfo {
    private String jdbcUrl;
    private String driverClassName;
    private String maximumPoolSize;
    private String username;
    private String password;
    private String type;

What am I missing? How do I convince the inversion of control container that it needs to inject an object and not a try to convert a string to an object? Is that even what's happening?

Upvotes: 1

Views: 2082

Answers (3)

Unmitigated provided a one-line answer, but the traditional mechanism (or one that simply avoids any Spring coupling at all with the implementation class) is to use an @Bean method in an @Configuration class, where you pass the properties object as a parameter and "unwind" it in the service's constructor:

@Bean
MyService(MyProperties props) {
  return new MyServiceImpl(props.getFoo(), props.getBar());
}

Note that your jdbc bits seem to duplicate the out-of-the-box auto-configuration for JDBC; prefer the defaults when practical.

Upvotes: 0

Unmitigated
Unmitigated

Reputation: 89404

Use the @ConstructorBinding annotation.

@ConfigurationProperties(prefix = "app")
@ConstructorBinding
public class AppConfig {
    private String name;
    private DataSourceInfo jdbc;
    public AppConfig(String name, DataSourceInfo jdbc) {
        this.name = name;
        this.jdbc = jdbc;
    }
}

Upvotes: 3

Eugene
Eugene

Reputation: 120978

If you really want to use @Value, it must have the form: @Value("app.name") String name; but you do not want to use it anyway. Spring favors @ConfigurationProperties for a long time now, and @Value does not work with relaxed binding either.

Upvotes: 0

Related Questions