16

I have a class of the sort :

@Data
@Component
@ConfigurationProperties("a.config.props")
public class ClientProperties {

    private String hostname;
    private String port;
    private String baseUrl;
    private String endpoint;
}

I have tens of property contexts but for 1 line i have to repeat the whole class. Is there a simple and elegant way to have one class and somehow pass the property context dynamically (maybe an array or something of the sort) so that i use the same class.

3 Answers 3

25

You can just have a single class that describes your numerous & identical property blocks;

@Data
public class ClientProperties {
    private String hostname;
    private String port;
    private String baseUrl;
    private String endpoint;
}

Then refer to it as such to link that single class with different property blocks;

@Configuration
public class PropertyConfigurer {

    @Bean
    @ConfigurationProperties("props.one")
    private ClientProperties propsOne() {
        return new ClientProperties();
    }

    @Bean
    @ConfigurationProperties("props.two")
    private ClientProperties propsTwo() {
        return new ClientProperties();
    }

    @Bean
    @ConfigurationProperties("props.three")
    private ClientProperties propsThree() {
        return new ClientProperties();
    }
}

Then you can access them via their method names as qualifiers;

@Component
public class SomeService {

    @Autowired
    private ClientProperties propsOne;
    @Autowired
    private ClientProperties propsTwo;
    @Autowired
    private ClientProperties propsThree;

    ... some logic
}
7
  • I would also require many instances so the scope would be prototype for clientProperties. So can i get rid of the class PropertyConfigurer ?
    – Raghuveer
    Commented Aug 23, 2019 at 7:05
  • @Raghuveer why get rid of the class? I don't understand your point. You have to define like that to bind that class structure for each property block with its path in property file. You cannot just have the ClientProperties with @Component & refer to multiple blocks.
    – buræquete
    Commented Aug 23, 2019 at 7:08
  • thanks for your quick response. I want all the beans to be spring managed, thats why i want prototype but i want each property context to be bound with the class and instantiated. Any spring tricks ?
    – Raghuveer
    Commented Aug 23, 2019 at 7:11
  • @Raghuveer prototype scope does not make beans Spring managed, all beans are already managed by Spring. prototype will only create a new instance of the bean for every new autowiring (read about it here). My answer should be enough for you.
    – buræquete
    Commented Aug 23, 2019 at 7:13
  • 1
    @Raghuveer I don't understand, it is default Spring bean declaration through methods. It is given as the default method in the documentation for declaring beans for properties. There is no any such trick, you can do like Sumant suggesting, but that also creates many classes on top of the abstract class which is a mess. The cleanest solution is this one in my opinion.
    – buræquete
    Commented Aug 23, 2019 at 8:24
7

I would suggest to create an abstract class with all the properties and extend the abstract class for as many property variants you have.

@Data
public abstract class BaseClientProperties {
    private String hostname;
    private String port;
    private String baseUrl;
    private String endpoint;
}

@Configuration
@ConfigurationProperties("a.config.props1")
public class Client1Properties extends BaseClientProperties{

}

@Configuration
@ConfigurationProperties("a.config.props2")
public class Client2Properties extends BaseClientProperties{
}

Use them as below:

@Service
public class SomeService {

    @Autowired
    private Client1Properties client1Properties;
    @Autowired
    private Client2Properties client2Properties;

    ... service logic
}
1
  • IMHO this is better as it uses abstraction properly. Commented Jun 21 at 16:03
0

Suppose you want hostname as a list to your properties file then you can rearrange your code like this:

@Component
@ConfigurationProperties("a.config.props")
@Data
public class ClientProperties {
    private List<String>  hostname;
    private String port;
    private String baseUrl;
    private String endpoint;
}

You can then place your configuration properties in your application.properties file like this:

a.config.props.hostname[0]=host1.com
a.config.props.hostname[1]=host2.com
a.config.props.hostname[2]=host3.com
a.config.props.port=8080
a.config.props.baseUrl=baseurl
a.config.props.endpoint=/thisendpoint

Then simply @Autowire your ClientProperties to the class you want and call

List<String> hostnames = clientProperties.getHostname();

You can utilize their values as required then. If I understand your question correctly I think this will answer it. Let me know if you need more information.

Not the answer you're looking for? Browse other questions tagged or ask your own question.