OutOfMind
OutOfMind

Reputation: 924

Inject implementation based on command line argument

Is there a way to inject a particular interface implementation based on a command line argument in Spring Boot?

I have a data loading app and based on the command line argument I need to load a specific type of data.

Here is my main class and CommandLineRunner:

@SpringBootApplication
public class DataLoadersApplication implements CommandLineRunner {

    private Type1LoadProcess type1LoadProcess;
    private Type2LoadProcess type2LoadProcess;

    public DataLoadersApplication(Type1LoadProcess type1LoadProcess,
      Type2LoadProcess type2LoadProcess) {
        this.type1LoadProcess = type1LoadProcess;
        this.type2LoadProcess = type2LoadProcess;
    }

    public static void main(String[] args) {
        SpringApplication.run(DataLoadersApplication.class, args);
    }

    @Override
    public void run(String... args) {
        if (args[0].equalsIgnoreCase("load-type1")) {
            type1LoadProcess.process();
        } else if (args[0].equalsIgnoreCase("load-type2")) {
            type2LoadProcess.process();
        } 
    }
}

Is there a way where I create a DataLoadeProcess interface with two implementations Type1DataLoadProcess and Type2DataLoadProcess and inject the implementaion in main class based on the commandline arg?

Upvotes: 1

Views: 848

Answers (3)

zapl
zapl

Reputation: 63955

A complete example for this is

@SpringBootApplication
public class DataLoadersApplication implements CommandLineRunner {
    public interface LoadProcess {
        void doLoad();
    }

    @Component // default that exists unconditionally in any profile
    static class Type1LoadProcess implements LoadProcess {
        @Override public void doLoad() { System.out.println("Load1"); }
    }

    @Profile("type2") // this only exists in the type2 profile
    @Primary          // if it exists it gets picked over others
    @Component
    static class Type2LoadProcess implements LoadProcess {
        @Override public void doLoad() { System.out.println("Load2"); }
    }
    // need a 3rd? @Profile("type3") @Primary @Component

    @Autowired  // need one of them here
    private LoadProcess loadProcess;

    @Override
    public void run(String... args) {
        loadProcess.doLoad();
    }

    public static void main(String[] args) {
        SpringApplication.run(DataLoadersApplication.class, args);
    }
}

This makes use of profiles and uses the primary bean mechanism to allow for a default implementation when no profile is specified.

You can then select which profile is used via any of the options listed at https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html e.g. by setting an environemnt variable

SPRING_PROFILES_ACTIVE=type2 java -jar myApp.jar

using a property

java -Dspring.profiles.active=type2 java -jar myApp.jar

or even a parameter

java -jar myApp.jar --spring.profiles.active=type2

when you want the type2 implementation. You can still put "type1" as active profile even though it is nowhere defined. It will still do the right thing and use the type1 code since that's the default.

Upvotes: 2

Simon Martinelli
Simon Martinelli

Reputation: 36203

You can use Spring profiles to achieve your goal: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

Create the interface DataLoadProcess

Then the classes:

@Component
@Profile("type1")
public class Type1LoadProcess implements DataLoadProcess {
}

@Component
@Profile("type2")
public class Type2LoadProcess implements DataLoadProcess {
}

Then you can inject the interface type like:

@Autowired 
DataLoadProcess dataLoadProcessor;

And now you can start your application with one of the profiles for example with a system property set:

-Dspring.profiles.active=type1

Upvotes: 5

anothernode
anothernode

Reputation: 5397

I would use Spring profiles for this. Just turn your implementations into Spring Beans and then load the desired Bean based on an active profile.

When you then specify the active profile(s) as command line parameters when starting the app the respective Bean should get used.

Upvotes: 2

Related Questions