0

I have a service which caches the response. I use a simple Map for caching the response.

I also have two scopes: @RequestScope and @GrpcScope. @RequestScope is obviously for requests received from rest controller whereas @GrpcScope is for grpc requests. My service doesn't know what scope it is currently running in. Both (grpc and rest) controllers uses the same service and there could be only one scope active at runtime.

I want FooService to use RequestScopedFooCache in if the the scope is @RequestScope and to use GrpcScopedFooCache if the scope is @GrpcScope. How can I do that?

// for RequestScope
@RestController
public class FooRestController{
    @Autowired
    FooService fooService;
    
    @GetMapping
    public BarResponse getFoo(){
        return fooService.fooRequest(...)
    }
}

// for @GrpcScope
@GrpcController 
public class FooGrpcController{
    @Autowired
    FooService fooService;
    
    public BarResponse getFoo(){
        return fooService.fooRequest(...)
    }
}

@Service
public class FooService {

    @Autowired
    FooCache cache;  // doesn't know which bean to autowire however there could be only one of them at runtime
    
    BarResponse fooRequest(String id) {
        if(cache.get(id))
            ...
    }
}
public interface FooCache {
    ...
}

@Component
@RequestScope
public class RequestScopedFooCache implements FooCache {
    ...
}

@Component
@GrpcScope
public class GrpcScopedFooCache implements FooCache {
    ...
}

Using SpringBoot version 2.4.+

hevi
  • 2,432
  • 1
  • 32
  • 51
  • Can you clarify `@RequestScope` and `@GrpcScope` here? `@RequestScope` is a Spring Boot annotation (which doesn't seem to make sense here) but I can't find any reference to `@GrpcScope`. Also, what determines which scope is active in a particular runtime? – DarkMatter Jan 06 '21 at 19:44
  • GrpcScope is a custom scope https://yidongnan.github.io/grpc-spring-boot-starter/en/server/contextual-data.html – hevi Jan 07 '21 at 09:20

1 Answers1

0

Not exactly what you are asking for but I would probably create my own bean methods for FooService and give them qualifying names.

Changed FooService to take FooCache as a constructor:

public class FooService {
    private final FooCache cache;

    public FooService(FooCache cache) {
        this.cache = cache;
    }

    BarResponse fooRequest(String id) {
        if (cache.get(id))
            ...
    }
}

Give the FooCache implementations qualifying names:

public interface FooCache {
    ...
}

@Component("GRPC_CACHE")
@RequestScope
public class RequestScopedFooCache implements FooCache {
    ...
}

@Component("REQUEST_CACHE")
@GrpcScope
public class GrpcScopedFooCache implements FooCache {
    ...
}

A bean manager to setup the FooService beans:

class FooServiceBeanManager {
    @Bean("GRPC_SERVICE")
    public FooService grpcFooService(@Qualifier("GRPC_CACHE") FooCache fooCache) {
        return new FooService(fooCache);
    }

    @Bean("REQUEST_SERVICE")
    public FooService requestFooService(@Qualifier("GRPC_CACHE") FooCache fooCache) {
        return new FooService(fooCache);
    }
}

Specify which bean you want in your controllers using @Qualifer:

// for RequestScope
@RestController
public class FooRestController {

    private final FooService fooService;

    @Autowired
    public FooRestController(@Qualifier("REQUEST_SERVICE") FooService fooService) {
        this.fooService = fooService;
    }


    @GetMapping
    public BarResponse getFoo() {
        return fooService.fooRequest(...)
    }
}

// for @GrpcScope
@GrpcController
public class FooGrpcController {

    private final FooService fooService;

    @Autowired
    public FooGrpcController(@Qualifier("GRPC_SERVICE") FooService fooService) {
        this.fooService = fooService;
    }


    public BarResponse getFoo() {
        return fooService.fooRequest(...)
    }
}

I changed the controller classes to have @Autowired constructors instead of fields. It is the recommended usage (source: Spring @Autowire on Properties vs Constructor)

I also personally prefer to have my own @Bean methods as much as possible because it gives me much more control.

DarkMatter
  • 1,007
  • 7
  • 13
  • I see your point but what I need is not what you did. I just gave an example `fooService` is being called directly from the `Controllers`. In fact fooService is being called from other services and I cannot pass the qualifier to all of them. – hevi Jan 06 '21 at 19:01
  • So the classes that need the service (the controllers in this examle) don't know which one they need themselves, or you are not able to introcude `@Qualifer` in them, is that correct? What aspect of the controllers is the defining factor? The annotation? Just trying to understand our case a bit better. – DarkMatter Jan 06 '21 at 19:16
  • yes the service calling the fooService doesn't know which scope it is in. and yes i cannot introduce a qualifier. you can think like I am writing a library and use the request scoped cache if the scope is request scope otherwise grpcscope – hevi Jan 06 '21 at 19:28