Pr0pagate
Pr0pagate

Reputation: 199

How to Dynamically Determine Service Class To Use At Runtime

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

Answers (3)

Sami Korhonen
Sami Korhonen

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

Jayesh
Jayesh

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

joy
joy

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

Related Questions