2

I have a myapp.properties file with key-value pairs defined as:

prefix.int-field=123
prefix.string-field=asdf
prefix.custom-type-field=my.package.CustomType

I am trying to inject these properties by using a @Value annotation in a following class:

@PropertySource(value = "classpath:myapp.properties")
@Component
class MySettings {
    @Value("${prefix.int-field}")
    private int intField;

    @Value("${prefix.string-field}")
    private String stringField;

    @Value("${prefix.custom-type-field}") // <-- this is the problem
    private CustomInterface customField;
}

class CustomType implements CustomInterface {...}

interface CustomInterface {...}

Now, intField and stringField get initialized with the desired values as expected, but customField throws an exception:

Caused by: java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [my.package.CustomInterface]: no matching editors or conversion strategy found
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:303) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:125) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:61) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]

How can I convert the text property values to my custom type?

I tried to consult the documentation, but I fail to see the correct way of doing it. I am using Spring Boot 1.3.6.

radoh
  • 4,554
  • 5
  • 30
  • 45
  • 1
    have you tried to add your custom Converter? It should be a class implementing Converter, more details http://stackoverflow.com/questions/34239585/how-to-register-custom-converters-in-spring-boot – hi_my_name_is Oct 17 '16 at 07:55
  • Why don't you just inject it like a bean? – grape_mao Oct 17 '16 at 07:57
  • @freakman I haven't tried that yet, I'm new to Spring and confused about things. I'll give it a shot – radoh Oct 17 '16 at 08:06
  • @grape_mao but I want `customField` to be configurable through the properties file, that is, if I later choose to use a different implementation of `CustomInterface` I can just change the properties file... – radoh Oct 17 '16 at 08:08

1 Answers1

1

To resolve your immediate issue you want to look at the @PostConstruct option on the bean. That will allow you to act on things before the bean is made available to the context.

@PropertySource(value = "classpath:myapp.properties")
@Component
class MySettings {
    @Value("${prefix.int-field}")
    private int intField;

    @Value("${prefix.string-field}")
    private String stringField;

    @Value("${prefix.custom-type-field}")
    private String customFieldType;

    private CustomInterface customField;

    @PostConstruct
    public void init() {
        customField = (CustomInterface) Class.forName(customFieldType).newInstance(); // short form... will need checks that it finds the class and can create a new instance
    }
}

class CustomType implements CustomInterface {...}

interface CustomInterface {...}

I am curious if you might want to use the @Configuration annotation on a class and create an instance of the CustomInterface that is made available as a bean on the Spring ApplicationContext. To do that you would instead do something like this:

@Component
@ConfigurationProperties(prefix = "prefix")
class MySettings {
    private int intField;

    private String stringField;

    private String customTypeField;

    // getters and setters
}

That would then be used in an @Configuration class:

@Configuration
class MyConfiguration {
    @Bean
    public CustomInterface customInterface(MySettings mySettings) {
        return (CustomInterface) Class.forName(mySettings.getCustomTypeField()).newInstance();
    }
}

At this point you would now have an instantiated bean for CustomInterface that you can have Spring autowire into other objects.

radoh
  • 4,554
  • 5
  • 30
  • 45
Shawn Clark
  • 3,330
  • 2
  • 18
  • 30
  • Thanks for a nice answer. I've tried the 2 solutions you suggested and they both work nicely. (though the first one is more verbose, it offers the ability to ctrl+click (intellij idea) on the `${...}` value string which takes you straight to the property in `.properties` file. I'll accept your answer although I may not end up using it, since my custom types can have constructor parameters, so I'll probably have to approach it totally differently. – radoh Oct 19 '16 at 13:27
  • @radoh I realize this is "a bit" old question, but it'd still be nice to see you post an answer on how you eventually managed to include those constructor parameters... – ZeroOne Jan 25 '22 at 07:24