Dynamic dependency injection for multiple implementations of the same interface with Spring MVC










0














I am working on a REST API where I have an interface that defines a list of methods which are implemented by 4 different classes, with the possibility of adding many more in the future.



When I receive an HTTP request from the client there is some information included in the URL which will determine which implementation needs to be used.



Within my controller, I would like to have the end-point method contain a switch statement that checks the URL path variable and then uses the appropriate implementation.



I know that I can define and inject the concrete implementations into the controller and then insert which one I would like to use in each particular case in the switch statement, but this doesn't seem very elegant or scalable for 2 reasons:



  1. I now have to instantiate all of the services, even though I only need to use one.


  2. The code seems like it could be much leaner since I am literally calling the same method that is defined in the interface with the same parameters and while in the example it is not really an issue, but in the case that the list of implementations grows ... so does the number of cases and redundant code.


Is there a better solution to solve this type of situation? I am using SpringBoot 2 and JDK 10, ideally, I'd like to implement the most modern solution.



My Current Approach



@RequestMapping(Requests.MY_BASE_API_URL)
public class MyController

//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;

//== CONSTRUCTORS ==
@Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3)
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;



//== REQUEST MAPPINGS ==
@GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(@PathVariable String source,
@RequestParam String start,
@RequestParam String end)

source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source))
switch(source)
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;

else
//An invalid source path variable was recieved


//Return something after additional processing
return null;











share|improve this question





















  • I think, If source possibilities are few you can have separate rest mapping api's for each source. or if there are more source possibilities, then check if there is a way of incorporating logic of handling multiple source in service layer itself rather at api level.
    – Hrudayanath
    Nov 13 '18 at 4:47











  • I thought about that as well and I actually had the controller set up that way initially but I ran into the same issue since I am autowiring the implementations into the constructor. In this particular case, if I were to add a different mapping within the same controller for each source then I'd have even more boilerplate code. The only option with this approach would be to use a different controller for each source, which is doable ... but it seems more like a workaround than a solution but if I can't find anything better I may take that approach.
    – oznomal
    Nov 13 '18 at 4:55










  • Check if you can design one service implementation which can handle the conditional logic for different sources thereby you can have logic in one class and one corresponding api by passing source param to serviceImpl.
    – Hrudayanath
    Nov 13 '18 at 5:05










  • Delegate the detection to the services instead. Inject a list of your services (interface). Then in your method loop through your list of services, and call a method to see if they can handle the request/source. When you found the correct one, use it. That way you have an extensible mechanism.
    – M. Deinum
    Nov 13 '18 at 14:10















0














I am working on a REST API where I have an interface that defines a list of methods which are implemented by 4 different classes, with the possibility of adding many more in the future.



When I receive an HTTP request from the client there is some information included in the URL which will determine which implementation needs to be used.



Within my controller, I would like to have the end-point method contain a switch statement that checks the URL path variable and then uses the appropriate implementation.



I know that I can define and inject the concrete implementations into the controller and then insert which one I would like to use in each particular case in the switch statement, but this doesn't seem very elegant or scalable for 2 reasons:



  1. I now have to instantiate all of the services, even though I only need to use one.


  2. The code seems like it could be much leaner since I am literally calling the same method that is defined in the interface with the same parameters and while in the example it is not really an issue, but in the case that the list of implementations grows ... so does the number of cases and redundant code.


Is there a better solution to solve this type of situation? I am using SpringBoot 2 and JDK 10, ideally, I'd like to implement the most modern solution.



My Current Approach



@RequestMapping(Requests.MY_BASE_API_URL)
public class MyController

//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;

//== CONSTRUCTORS ==
@Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3)
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;



//== REQUEST MAPPINGS ==
@GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(@PathVariable String source,
@RequestParam String start,
@RequestParam String end)

source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source))
switch(source)
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;

else
//An invalid source path variable was recieved


//Return something after additional processing
return null;











share|improve this question





















  • I think, If source possibilities are few you can have separate rest mapping api's for each source. or if there are more source possibilities, then check if there is a way of incorporating logic of handling multiple source in service layer itself rather at api level.
    – Hrudayanath
    Nov 13 '18 at 4:47











  • I thought about that as well and I actually had the controller set up that way initially but I ran into the same issue since I am autowiring the implementations into the constructor. In this particular case, if I were to add a different mapping within the same controller for each source then I'd have even more boilerplate code. The only option with this approach would be to use a different controller for each source, which is doable ... but it seems more like a workaround than a solution but if I can't find anything better I may take that approach.
    – oznomal
    Nov 13 '18 at 4:55










  • Check if you can design one service implementation which can handle the conditional logic for different sources thereby you can have logic in one class and one corresponding api by passing source param to serviceImpl.
    – Hrudayanath
    Nov 13 '18 at 5:05










  • Delegate the detection to the services instead. Inject a list of your services (interface). Then in your method loop through your list of services, and call a method to see if they can handle the request/source. When you found the correct one, use it. That way you have an extensible mechanism.
    – M. Deinum
    Nov 13 '18 at 14:10













0












0








0


1





I am working on a REST API where I have an interface that defines a list of methods which are implemented by 4 different classes, with the possibility of adding many more in the future.



When I receive an HTTP request from the client there is some information included in the URL which will determine which implementation needs to be used.



Within my controller, I would like to have the end-point method contain a switch statement that checks the URL path variable and then uses the appropriate implementation.



I know that I can define and inject the concrete implementations into the controller and then insert which one I would like to use in each particular case in the switch statement, but this doesn't seem very elegant or scalable for 2 reasons:



  1. I now have to instantiate all of the services, even though I only need to use one.


  2. The code seems like it could be much leaner since I am literally calling the same method that is defined in the interface with the same parameters and while in the example it is not really an issue, but in the case that the list of implementations grows ... so does the number of cases and redundant code.


Is there a better solution to solve this type of situation? I am using SpringBoot 2 and JDK 10, ideally, I'd like to implement the most modern solution.



My Current Approach



@RequestMapping(Requests.MY_BASE_API_URL)
public class MyController

//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;

//== CONSTRUCTORS ==
@Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3)
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;



//== REQUEST MAPPINGS ==
@GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(@PathVariable String source,
@RequestParam String start,
@RequestParam String end)

source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source))
switch(source)
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;

else
//An invalid source path variable was recieved


//Return something after additional processing
return null;











share|improve this question













I am working on a REST API where I have an interface that defines a list of methods which are implemented by 4 different classes, with the possibility of adding many more in the future.



When I receive an HTTP request from the client there is some information included in the URL which will determine which implementation needs to be used.



Within my controller, I would like to have the end-point method contain a switch statement that checks the URL path variable and then uses the appropriate implementation.



I know that I can define and inject the concrete implementations into the controller and then insert which one I would like to use in each particular case in the switch statement, but this doesn't seem very elegant or scalable for 2 reasons:



  1. I now have to instantiate all of the services, even though I only need to use one.


  2. The code seems like it could be much leaner since I am literally calling the same method that is defined in the interface with the same parameters and while in the example it is not really an issue, but in the case that the list of implementations grows ... so does the number of cases and redundant code.


Is there a better solution to solve this type of situation? I am using SpringBoot 2 and JDK 10, ideally, I'd like to implement the most modern solution.



My Current Approach



@RequestMapping(Requests.MY_BASE_API_URL)
public class MyController

//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;

//== CONSTRUCTORS ==
@Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3)
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;



//== REQUEST MAPPINGS ==
@GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(@PathVariable String source,
@RequestParam String start,
@RequestParam String end)

source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source))
switch(source)
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;

else
//An invalid source path variable was recieved


//Return something after additional processing
return null;








java spring spring-mvc spring-boot dependency-injection






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 13 '18 at 4:39









oznomal

71111




71111











  • I think, If source possibilities are few you can have separate rest mapping api's for each source. or if there are more source possibilities, then check if there is a way of incorporating logic of handling multiple source in service layer itself rather at api level.
    – Hrudayanath
    Nov 13 '18 at 4:47











  • I thought about that as well and I actually had the controller set up that way initially but I ran into the same issue since I am autowiring the implementations into the constructor. In this particular case, if I were to add a different mapping within the same controller for each source then I'd have even more boilerplate code. The only option with this approach would be to use a different controller for each source, which is doable ... but it seems more like a workaround than a solution but if I can't find anything better I may take that approach.
    – oznomal
    Nov 13 '18 at 4:55










  • Check if you can design one service implementation which can handle the conditional logic for different sources thereby you can have logic in one class and one corresponding api by passing source param to serviceImpl.
    – Hrudayanath
    Nov 13 '18 at 5:05










  • Delegate the detection to the services instead. Inject a list of your services (interface). Then in your method loop through your list of services, and call a method to see if they can handle the request/source. When you found the correct one, use it. That way you have an extensible mechanism.
    – M. Deinum
    Nov 13 '18 at 14:10
















  • I think, If source possibilities are few you can have separate rest mapping api's for each source. or if there are more source possibilities, then check if there is a way of incorporating logic of handling multiple source in service layer itself rather at api level.
    – Hrudayanath
    Nov 13 '18 at 4:47











  • I thought about that as well and I actually had the controller set up that way initially but I ran into the same issue since I am autowiring the implementations into the constructor. In this particular case, if I were to add a different mapping within the same controller for each source then I'd have even more boilerplate code. The only option with this approach would be to use a different controller for each source, which is doable ... but it seems more like a workaround than a solution but if I can't find anything better I may take that approach.
    – oznomal
    Nov 13 '18 at 4:55










  • Check if you can design one service implementation which can handle the conditional logic for different sources thereby you can have logic in one class and one corresponding api by passing source param to serviceImpl.
    – Hrudayanath
    Nov 13 '18 at 5:05










  • Delegate the detection to the services instead. Inject a list of your services (interface). Then in your method loop through your list of services, and call a method to see if they can handle the request/source. When you found the correct one, use it. That way you have an extensible mechanism.
    – M. Deinum
    Nov 13 '18 at 14:10















I think, If source possibilities are few you can have separate rest mapping api's for each source. or if there are more source possibilities, then check if there is a way of incorporating logic of handling multiple source in service layer itself rather at api level.
– Hrudayanath
Nov 13 '18 at 4:47





I think, If source possibilities are few you can have separate rest mapping api's for each source. or if there are more source possibilities, then check if there is a way of incorporating logic of handling multiple source in service layer itself rather at api level.
– Hrudayanath
Nov 13 '18 at 4:47













I thought about that as well and I actually had the controller set up that way initially but I ran into the same issue since I am autowiring the implementations into the constructor. In this particular case, if I were to add a different mapping within the same controller for each source then I'd have even more boilerplate code. The only option with this approach would be to use a different controller for each source, which is doable ... but it seems more like a workaround than a solution but if I can't find anything better I may take that approach.
– oznomal
Nov 13 '18 at 4:55




I thought about that as well and I actually had the controller set up that way initially but I ran into the same issue since I am autowiring the implementations into the constructor. In this particular case, if I were to add a different mapping within the same controller for each source then I'd have even more boilerplate code. The only option with this approach would be to use a different controller for each source, which is doable ... but it seems more like a workaround than a solution but if I can't find anything better I may take that approach.
– oznomal
Nov 13 '18 at 4:55












Check if you can design one service implementation which can handle the conditional logic for different sources thereby you can have logic in one class and one corresponding api by passing source param to serviceImpl.
– Hrudayanath
Nov 13 '18 at 5:05




Check if you can design one service implementation which can handle the conditional logic for different sources thereby you can have logic in one class and one corresponding api by passing source param to serviceImpl.
– Hrudayanath
Nov 13 '18 at 5:05












Delegate the detection to the services instead. Inject a list of your services (interface). Then in your method loop through your list of services, and call a method to see if they can handle the request/source. When you found the correct one, use it. That way you have an extensible mechanism.
– M. Deinum
Nov 13 '18 at 14:10




Delegate the detection to the services instead. Inject a list of your services (interface). Then in your method loop through your list of services, and call a method to see if they can handle the request/source. When you found the correct one, use it. That way you have an extensible mechanism.
– M. Deinum
Nov 13 '18 at 14:10












2 Answers
2






active

oldest

votes


















1














In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.



Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :



interface MyInterface() 
(...)
String name()



This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).



When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.



A simplistic implementation in Spring Boot could look like this:



@SpringBootApplication
public class DynamicDependencyInjectionForMultipleImplementationsApplication

public static void main(String args)
SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);


interface MyInterface
Object getStuff();


class Implementation1 implements MyInterface
@Override public Object getStuff()
return "foo";



class Implementation2 implements MyInterface
@Override public Object getStuff()
return "bar";



@Configuration
class Config

@Bean("getFoo")
Implementation1 implementation1()
return new Implementation1();


@Bean("getBar")
Implementation2 implementation2()
return new Implementation2();





@RestController
class Controller

private final Map<String, MyInterface> implementations;

Controller(Map<String, MyInterface> implementations)
this.implementations = implementations;


@GetMapping("/run/beanName")
Object runSelectedImplementation(@PathVariable String beanName)
return Optional.ofNullable(implementations.get(beanName))
.orElseThrow(UnknownImplementation::new)
.getStuff();


@ResponseStatus(BAD_REQUEST)
class UnknownImplementation extends RuntimeException






It passes the following tests:



@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests

@Autowired
private MockMvc mockMvc;

@Test
public void shouldCallImplementation1() throws Exception
mockMvc.perform(get("/run/getFoo"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("foo")));


@Test
public void shouldCallImplementation2() throws Exception
mockMvc.perform(get("/run/getBar"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("bar")));


@Test
public void shouldRejectUnknownImplementations() throws Exception
mockMvc.perform(get("/run/getSomethingElse"))
.andExpect(status().isBadRequest());







share|improve this answer






























    0














    Regarding two of your doubts :

    1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.

    2. You can use the exact Path mapping to get rid of switch case. For e.g. :



    @GetMapping("/specificRequest/value1")
    @GetMapping("/specificRequest/value2")
    @GetMapping("/specificRequest/value3")


    All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
    Hope this will help to make code more cleaner and elegant.



    There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :



    1. Makes it easy to write the test cases.

    2. Scaling the same without impacting any other source/service.

    3. Your code dealing the each source as separate entity from other sources.

    The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.






    share|improve this answer






















      Your Answer






      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "1"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













      draft saved

      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53273923%2fdynamic-dependency-injection-for-multiple-implementations-of-the-same-interface%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      1














      In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.



      Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :



      interface MyInterface() 
      (...)
      String name()



      This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).



      When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.



      A simplistic implementation in Spring Boot could look like this:



      @SpringBootApplication
      public class DynamicDependencyInjectionForMultipleImplementationsApplication

      public static void main(String args)
      SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);


      interface MyInterface
      Object getStuff();


      class Implementation1 implements MyInterface
      @Override public Object getStuff()
      return "foo";



      class Implementation2 implements MyInterface
      @Override public Object getStuff()
      return "bar";



      @Configuration
      class Config

      @Bean("getFoo")
      Implementation1 implementation1()
      return new Implementation1();


      @Bean("getBar")
      Implementation2 implementation2()
      return new Implementation2();





      @RestController
      class Controller

      private final Map<String, MyInterface> implementations;

      Controller(Map<String, MyInterface> implementations)
      this.implementations = implementations;


      @GetMapping("/run/beanName")
      Object runSelectedImplementation(@PathVariable String beanName)
      return Optional.ofNullable(implementations.get(beanName))
      .orElseThrow(UnknownImplementation::new)
      .getStuff();


      @ResponseStatus(BAD_REQUEST)
      class UnknownImplementation extends RuntimeException






      It passes the following tests:



      @RunWith(SpringRunner.class)
      @SpringBootTest
      @AutoConfigureMockMvc
      public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests

      @Autowired
      private MockMvc mockMvc;

      @Test
      public void shouldCallImplementation1() throws Exception
      mockMvc.perform(get("/run/getFoo"))
      .andExpect(status().isOk())
      .andExpect(content().string(containsString("foo")));


      @Test
      public void shouldCallImplementation2() throws Exception
      mockMvc.perform(get("/run/getBar"))
      .andExpect(status().isOk())
      .andExpect(content().string(containsString("bar")));


      @Test
      public void shouldRejectUnknownImplementations() throws Exception
      mockMvc.perform(get("/run/getSomethingElse"))
      .andExpect(status().isBadRequest());







      share|improve this answer



























        1














        In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.



        Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :



        interface MyInterface() 
        (...)
        String name()



        This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).



        When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.



        A simplistic implementation in Spring Boot could look like this:



        @SpringBootApplication
        public class DynamicDependencyInjectionForMultipleImplementationsApplication

        public static void main(String args)
        SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);


        interface MyInterface
        Object getStuff();


        class Implementation1 implements MyInterface
        @Override public Object getStuff()
        return "foo";



        class Implementation2 implements MyInterface
        @Override public Object getStuff()
        return "bar";



        @Configuration
        class Config

        @Bean("getFoo")
        Implementation1 implementation1()
        return new Implementation1();


        @Bean("getBar")
        Implementation2 implementation2()
        return new Implementation2();





        @RestController
        class Controller

        private final Map<String, MyInterface> implementations;

        Controller(Map<String, MyInterface> implementations)
        this.implementations = implementations;


        @GetMapping("/run/beanName")
        Object runSelectedImplementation(@PathVariable String beanName)
        return Optional.ofNullable(implementations.get(beanName))
        .orElseThrow(UnknownImplementation::new)
        .getStuff();


        @ResponseStatus(BAD_REQUEST)
        class UnknownImplementation extends RuntimeException






        It passes the following tests:



        @RunWith(SpringRunner.class)
        @SpringBootTest
        @AutoConfigureMockMvc
        public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests

        @Autowired
        private MockMvc mockMvc;

        @Test
        public void shouldCallImplementation1() throws Exception
        mockMvc.perform(get("/run/getFoo"))
        .andExpect(status().isOk())
        .andExpect(content().string(containsString("foo")));


        @Test
        public void shouldCallImplementation2() throws Exception
        mockMvc.perform(get("/run/getBar"))
        .andExpect(status().isOk())
        .andExpect(content().string(containsString("bar")));


        @Test
        public void shouldRejectUnknownImplementations() throws Exception
        mockMvc.perform(get("/run/getSomethingElse"))
        .andExpect(status().isBadRequest());







        share|improve this answer

























          1












          1








          1






          In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.



          Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :



          interface MyInterface() 
          (...)
          String name()



          This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).



          When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.



          A simplistic implementation in Spring Boot could look like this:



          @SpringBootApplication
          public class DynamicDependencyInjectionForMultipleImplementationsApplication

          public static void main(String args)
          SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);


          interface MyInterface
          Object getStuff();


          class Implementation1 implements MyInterface
          @Override public Object getStuff()
          return "foo";



          class Implementation2 implements MyInterface
          @Override public Object getStuff()
          return "bar";



          @Configuration
          class Config

          @Bean("getFoo")
          Implementation1 implementation1()
          return new Implementation1();


          @Bean("getBar")
          Implementation2 implementation2()
          return new Implementation2();





          @RestController
          class Controller

          private final Map<String, MyInterface> implementations;

          Controller(Map<String, MyInterface> implementations)
          this.implementations = implementations;


          @GetMapping("/run/beanName")
          Object runSelectedImplementation(@PathVariable String beanName)
          return Optional.ofNullable(implementations.get(beanName))
          .orElseThrow(UnknownImplementation::new)
          .getStuff();


          @ResponseStatus(BAD_REQUEST)
          class UnknownImplementation extends RuntimeException






          It passes the following tests:



          @RunWith(SpringRunner.class)
          @SpringBootTest
          @AutoConfigureMockMvc
          public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests

          @Autowired
          private MockMvc mockMvc;

          @Test
          public void shouldCallImplementation1() throws Exception
          mockMvc.perform(get("/run/getFoo"))
          .andExpect(status().isOk())
          .andExpect(content().string(containsString("foo")));


          @Test
          public void shouldCallImplementation2() throws Exception
          mockMvc.perform(get("/run/getBar"))
          .andExpect(status().isOk())
          .andExpect(content().string(containsString("bar")));


          @Test
          public void shouldRejectUnknownImplementations() throws Exception
          mockMvc.perform(get("/run/getSomethingElse"))
          .andExpect(status().isBadRequest());







          share|improve this answer














          In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.



          Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :



          interface MyInterface() 
          (...)
          String name()



          This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).



          When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.



          A simplistic implementation in Spring Boot could look like this:



          @SpringBootApplication
          public class DynamicDependencyInjectionForMultipleImplementationsApplication

          public static void main(String args)
          SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);


          interface MyInterface
          Object getStuff();


          class Implementation1 implements MyInterface
          @Override public Object getStuff()
          return "foo";



          class Implementation2 implements MyInterface
          @Override public Object getStuff()
          return "bar";



          @Configuration
          class Config

          @Bean("getFoo")
          Implementation1 implementation1()
          return new Implementation1();


          @Bean("getBar")
          Implementation2 implementation2()
          return new Implementation2();





          @RestController
          class Controller

          private final Map<String, MyInterface> implementations;

          Controller(Map<String, MyInterface> implementations)
          this.implementations = implementations;


          @GetMapping("/run/beanName")
          Object runSelectedImplementation(@PathVariable String beanName)
          return Optional.ofNullable(implementations.get(beanName))
          .orElseThrow(UnknownImplementation::new)
          .getStuff();


          @ResponseStatus(BAD_REQUEST)
          class UnknownImplementation extends RuntimeException






          It passes the following tests:



          @RunWith(SpringRunner.class)
          @SpringBootTest
          @AutoConfigureMockMvc
          public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests

          @Autowired
          private MockMvc mockMvc;

          @Test
          public void shouldCallImplementation1() throws Exception
          mockMvc.perform(get("/run/getFoo"))
          .andExpect(status().isOk())
          .andExpect(content().string(containsString("foo")));


          @Test
          public void shouldCallImplementation2() throws Exception
          mockMvc.perform(get("/run/getBar"))
          .andExpect(status().isOk())
          .andExpect(content().string(containsString("bar")));


          @Test
          public void shouldRejectUnknownImplementations() throws Exception
          mockMvc.perform(get("/run/getSomethingElse"))
          .andExpect(status().isBadRequest());








          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Dec 10 '18 at 15:15

























          answered Nov 18 '18 at 10:06









          Apokralipsa

          1,601815




          1,601815























              0














              Regarding two of your doubts :

              1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.

              2. You can use the exact Path mapping to get rid of switch case. For e.g. :



              @GetMapping("/specificRequest/value1")
              @GetMapping("/specificRequest/value2")
              @GetMapping("/specificRequest/value3")


              All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
              Hope this will help to make code more cleaner and elegant.



              There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :



              1. Makes it easy to write the test cases.

              2. Scaling the same without impacting any other source/service.

              3. Your code dealing the each source as separate entity from other sources.

              The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.






              share|improve this answer



























                0














                Regarding two of your doubts :

                1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.

                2. You can use the exact Path mapping to get rid of switch case. For e.g. :



                @GetMapping("/specificRequest/value1")
                @GetMapping("/specificRequest/value2")
                @GetMapping("/specificRequest/value3")


                All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
                Hope this will help to make code more cleaner and elegant.



                There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :



                1. Makes it easy to write the test cases.

                2. Scaling the same without impacting any other source/service.

                3. Your code dealing the each source as separate entity from other sources.

                The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.






                share|improve this answer

























                  0












                  0








                  0






                  Regarding two of your doubts :

                  1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.

                  2. You can use the exact Path mapping to get rid of switch case. For e.g. :



                  @GetMapping("/specificRequest/value1")
                  @GetMapping("/specificRequest/value2")
                  @GetMapping("/specificRequest/value3")


                  All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
                  Hope this will help to make code more cleaner and elegant.



                  There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :



                  1. Makes it easy to write the test cases.

                  2. Scaling the same without impacting any other source/service.

                  3. Your code dealing the each source as separate entity from other sources.

                  The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.






                  share|improve this answer














                  Regarding two of your doubts :

                  1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.

                  2. You can use the exact Path mapping to get rid of switch case. For e.g. :



                  @GetMapping("/specificRequest/value1")
                  @GetMapping("/specificRequest/value2")
                  @GetMapping("/specificRequest/value3")


                  All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
                  Hope this will help to make code more cleaner and elegant.



                  There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :



                  1. Makes it easy to write the test cases.

                  2. Scaling the same without impacting any other source/service.

                  3. Your code dealing the each source as separate entity from other sources.

                  The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Nov 14 '18 at 5:51

























                  answered Nov 13 '18 at 4:54









                  bittu

                  332110




                  332110



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53273923%2fdynamic-dependency-injection-for-multiple-implementations-of-the-same-interface%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      27

                      Top Tejano songwriter Luis Silva dead of heart attack at 64

                      2013 DreamHack Counter-Strike: Global Offensive Championship