2

Suppose I have a class MailConsoleService and a class MailSMTPService, both implement the MailService interface. I have a class EmailJob which loads the users from a db and send an email through a MailService instance injected by Spring.

How could I read a properties and determine at runtime which implementation of MailService to inject? The properties could change at any time the app is running, obviously.

I've thought about to create a factory bean which returns the right instance from the spring container to EmailJob but I don't know how to implement this.

Note: All my beans are configured to Singleton scope, so I guess I'll have to change to Prototype EmailJob at least.

Note 2: In the factory bean how could I avoid to read the properties file each time?

Thanks!

imarban
  • 1,017
  • 1
  • 9
  • 24
  • You should look into Spring's `@Conditional` annotation, which lets you easily specify when to activate a bean or configuration class. Spring Boot provides a number of useful implementations, particularly `@ConditionalOnProperty`. – chrylis -cautiouslyoptimistic- Jul 07 '15 at 23:29

2 Answers2

1

You can do something like this:

@Component
public class Factory {

@Autowired
private MailService mailConsoleService;

@Autowired
private MailService mailSmtpService;

@Value("${mailServiceProperty}")
private String mailServiceProperty;

public MailService getMailService() {
    switch (mailServiceProperty) {
    case "CONSOLE":
        return mailConsoleService;

    case "SMTP":
        return mailSmtpService;
    }       
    return null;
}

}

Also, you need to inject properties using PropertyPlaceholderConfigurer

Naresh Vavilala
  • 598
  • 4
  • 14
  • This breaks the Open Closed Principle – imarban Jul 09 '15 at 16:21
  • The open closed problem would be eliminated if you use @Qualifier annotation. Please refer this question http://stackoverflow.com/questions/7812745/spring-qualifier-and-property-placeholder – Naresh Vavilala Jul 09 '15 at 17:27
0

I am not sure I fully understood your question. But based on what I understood, if you would like to get a bean at runtime from a properties file and the file could be changed at runtime, then the below is one way of doing this. You need a handle to the app context and get the bean name from the properties file.

The prototype scope has nothing to do with this. If you declare a bean of type prototype it means that you will get a new bean instance everytime you ask the app context for it.

@Component
public class EmailJob {  

    @Autowired
    private ApplicationContext appContext;

    public void sendEmail(){

        MailSender mailSender=(MailSender)appContext.getBean(<get bean name from properties file>);

        // do remaining things
    }


}
sethu
  • 8,181
  • 7
  • 39
  • 65
  • I know this way, I don't have to change the scope on this way but I had doubts about this way was the right one. Thank you – imarban Jul 07 '15 at 23:35
  • From a purist view, the app context should not be used after being created. You create the app context, it gives you the starting bean. From that point on, all beans should be loaded in as dependencies. You should never use the app context to get beans as such. But if your properties will change when the app is running, then the above is the only way I can think of. – sethu Jul 08 '15 at 01:23
  • Can I use this concept to read drools rules from .drl files or excel files? – Khwaja Sanjari Jan 09 '19 at 17:20