Reputation: 924
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
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
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
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