0

My service has a @Controller with multiple APIs.

Each API accepts a specific kind of object.

I would like to inject a single interface into a controller class, but have different implementations of the interface depending on the type of the input argument - is that possible?

@Controller
public class ApiClass{

    private final Service service;
    
    public ApiClass(Service service) {
        this.service = service;
    }

    public ResponseEntity<Response> apiFirst (Object1 object1) {
        return ResponseEntity.ok(service.process(object1));
    }
    
    public ResponseEntity<Response> apiTwo (Object2 object2) {
        return ResponseEntity.ok(service.process(object2));
    }
}

public interface Service <T extends OwnObjectClass>{
    void process (T object);
}

public class Implementation1 implements Service {
    @Override
    void process (Object1 object) {
        --some code;
    }
}

public class Implementation2 implements Service {
    @Override
    void process (Object2 object) {
        --some code;
    }
}

How to do it correctly so that for each implementation not to add a new injection to the ApiClass?

2 Answers 2

0

Spring will provide the primary bean for the interface implementation unless you use the @Qualifer annotation with the desired instance. The injected bean can not mutate to another instance.

If you don't want to use multiple injections in the controller, you can create a ServiceProvider and ask for a specific implementation each time.

Here is an example:

public class ApiClass{

    private final ServiceProvider provider;

    public ApiClass(ServiceProvider provider) {
        this.provider = provider;
    }


    public ResponseEntity<Response> apiFirst (Object1 object1) {
        return ResponseEntity.ok(provider.getService("Implementation1").process(object1));
    }

    public ResponseEntity<Response> apiTwo (Object2 object2) {
        return ResponseEntity.ok(provider.getService("Implementation2").process(object1));
    }
}

@org.springframework.stereotype.Service
public class ServiceProvider {

    private Map<String, Service> services;

    public ServiceProvider(List<Service> services) {
        this.services = services.stream()
            .collect(java.util.stream.Collectors.toMap(
                    Service::type,
                    service -> service
                )
            );
    }

    public Service getService(String type) {
        return services.get(type);
    }
}


interface Service<T extends OwnObjectClass> {

    String type();

    void process(T object);
}

@org.springframework.stereotype.Service("Implementation1")
class Implementation1 implements Service {

    @Override
    public String type() {
        return "Implementation1";
    }

    @Override
    public void process(OwnObjectClass object) {

    }
}

@org.springframework.stereotype.Service("Implementation2")
class Implementation2 implements Service {

    @Override
    public String type() {
        return "Implementation2";
    }

    @Override
    public void process(OwnObjectClass object) {

    }
}

You can change the string in the type for an Enum.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the suggested solution. Can I somehow, when specifying the generic <T extends OwnObjectClass> in the interface, in the implementation, use my own type for the argument included in the implemented method? I mean: org.springframework.stereotype.Service("Implementation2") class Implementation2 implements Service { Override public String type() { return "Implementation2"; } Override public void process(Object2 object) { } }
The restriction you put on the process method arguments is that Object2 must extend OwnObjectClass.
0

There is another way using HandlerMethodArgumentResolver where you can inject your dependency directly into the method definition.

Here is a nice article explaining it: https://reflectoring.io/spring-boot-argumentresolver/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.