Reputation: 199
For some context, I have a java app that takes a a JSON file and does some processing using custom rules based on the information inside the file. The problem I currently have is that I am trying to dynamically determine which service class to use to process the file at runtime. Below is my current implementation:
Interface
public interface DataService {
public void loadData(String path);
}
Implementation 1
@Service
public class ClassA implements DataService {
// some attributes...
public void loadData(String path) {
// implementation
}
}
Implementation 2
@Service
public class ClassB implements DataService {
// some attributes...
public void loadData(String path) {
// implementation
}
}
Implementation 3
@Service
public class ClassC implements DataService {
// some attributes...
public void loadData(String path) {
// implementation
}
}
Utilizing Class
@Service
public class DataRunner {
@Autowired
private DataService dataService;
@Value("${task.file}")
private String taskFile;
@PostConstruct
public void init() {
// process the incoming taskFile and derive an enum called DataSource
dataService.loadData("/example/file/location"); // what I wish would work
}
}
So as you can see the init method in the DataRunner class is just wishful thinking at this point. Is it possible through Spring Boot to dynamically determine which service class to use at run time? Or should I be doing something completely different to achieve what I want here?
Upvotes: 0
Views: 1085
Reputation: 1303
If you have hundreds of different processors, you could register (inject) them to a registry as a list. You can then iterate over list of registrations to see, which processor should be used (I decided to implement registration information as part of processor)
public interface DataProcessor {
public boolean supports(MyInput input);
public MyOutput process(MyInput input);
}
@Service
public class YesDataProcessor implements DataProcessor {
public boolean supports(MyInput input) {
return input.getSomething().equals("yes");
}
public MyOutput process(MyInput input) {
// ... transforming to output
return
}
}
@Service
public class NoDataProcessor implements DataProcessor {
public boolean supports(MyInput input) {
return input.getSomething().equals("no");
}
public MyOutput process(MyInput input) {
// ... transforming to output
return output;
}
}
@Service
public class MyDataProcessorRegistry {
@Autowired
private List<DataProcessor> processors;
public Optional<DataProcessor> getProcessor(MyInput input) {
return processors.stream().filter(p -> p.supports(input)).findFirst();
}
}
Upvotes: 2
Reputation: 999
You can introduce a resolver pattern to identify your implementation during runtime
@Service
public class DataServiceResolver{
@Autowired
private DataService classA;
@Autowired
private DataService classB;
@Autowired
private DataService classC;
public DataService resolve(Whatever whatever) {
//process your input and find the enum dataSource;
DataSource datasource = process(file);
DataService dataService;
switch datasource {
case A:
dataService = classA;
break;
case B:
dataService = classB;
break;
case C:
dataService = classC;
break;
default:
dataService = classB;
}
return dataService
}
}
and in your DataRunner class you use the resolver to find the needed implementation
@Service
public class DataRunner {
@Autowired
private DataServiceResolver dataServiceResolver;
@Value("${task.file}")
private String taskFile;
@PostConstruct
public void init() {
// process the incoming taskFile and derive an enum called DataSource
//call the resolver and resolve to the needed dataservice. whatever can be your taskFile, etc
DataService dataService = dataServiceResolver.resolve(whatever);
dataService.loadData("/example/file/location"); // what I wish would work
}
}
Upvotes: 3
Reputation: 327
Indirection is a great way to solve computing problems. I would inject a DataServiceFactory instead of DataService directly, and in that factory pass the DataSource enum. Have the factory return the appropriate instance of DataService.
Upvotes: 2