0

I have a (spring) service sending emails via different client adapters, depending on the environment they run on. So when i am on dev i want to use ClientAdapterA and on live ClientAdapterB. The usage looks like the following:

@Service
public class EmailService {

    @Autowired
    private ClientAdapter clientAdapter;

    public EmailResponse send(EmailRequest emailRequest) {
        return clientAdapter.sendMail(emailRequest);
    }
}

The configuration i want to happen via my application.yml:

mail:
  adapter: ClientAdapterA

I tried to use @Qualifier but that only allows use of one hard-coded adapter:

@Qualifier("ClientAdapterB")
@AutoWired
private ClientAdapter clientAdapter; 

I also tried creating a Config class which should provide the respective bean, but that didn't work either:

@Configuration
public class JavaBeansAdapterConfig {

    @Value("${mail.adapter}")
    private String adapterName;

    @Bean
    public ClientAdapter clientAdapterImplementation() throws ClassNotFoundException {
        ApplicationContext ctx = new AnnotationConfigApplicationContext();
        return (ClientAdapter)ctx.getAutowireCapableBeanFactory().createBean(Class.forName(adapterName));
    }
}

What am i doing wrong, or is there even a way do do it like i want to? Any help is appreciated, thank you.

Max
  • 180
  • 4
  • 13

2 Answers2

0

Define your Bean by using Class.forName(). But you need to set packageName as well. See below:

@Configuration
public class JavaBeansAdapterConfig {

    @Value("${mail.adapter}")
    private String adapterName;

    private final String packageName = "com.foo.bar";

    @Bean
    public ClientAdapter clientAdapterImplementation() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        return (ClientAdapter) Class.forName(packageName + "." + adapterName).newInstance();
    }
}
Monzurul Shimul
  • 8,132
  • 2
  • 28
  • 42
0

I tried with the package name, but this only fixed half the problems. I still could not seem to autowire the sub-dependencies, so I did some more digging and stumbled upon this solution.

My adapter configuration now looks like this:

@Component
@Setter
@Getter
@ConfigurationProperties(prefix = "mail.adapter")
public class ClientAdapterConfig {

    @Qualifier("mailClientA")
    @Autowired
    private ClientAdapter mailClientA;

    @Qualifier("mailClientB")
    @Autowired
    private ClientAdapter mailClientB;

    private String name;

    @Bean
    @Primary
    public ClientAdapter getAdapter()
    {
        ClientAdapter adapter = null;
        switch (name) {
            case "mailClientA":
                adapter = mailClientA;
                break;
            case "mailClientB":
                adapter = mailClientB;
                break;
            default:
                break;
        }

        return adapter;
    }
}

With the @Qualifier annotation I make sure that it puts in the Adapter I want.

Making the Bean @Primary then ensures it is correctly autowired into the EmailService (which remains unchanged compared to the block in the question)

I am aware that this is not actually a Configuration in the Spring sense, but at this point this is good-enough of a replacement for me.

Community
  • 1
  • 1
Max
  • 180
  • 4
  • 13