0

Let's assume I have this code:

@Controller
@RequestMapping("/something")
public class SomeController {
    @Autowired
    private SomeService aService;

    @RequestMapping("/doStuff")
    public void doStuff(@RequestParam("type") String type) {
        aService.doStuff();
    }
}

In my application I need to call a specific service depending on the specified type. All services implements the same interface. If I understand correctly SomeService cannot be an interface. I could use a service factory and instantiate the service depending on the type every time a new request is done, but this doesn't look very efficient.

Alternatively I could use a different controller for each different type of service (and encode the type in the REST URI), but this would imply a lot of code duplication since all the services basically implements the same interface.

My question is, assuming the called service depends on the passed parameter, what is the best pattern to adopt for this scenario?

saste
  • 710
  • 2
  • 11
  • 19
  • 2
    *If I understand correctly `SomeService` cannot be an interface* wrong, it can be an interface, in fact it should be and Spring will inject it with an instance of a class implementation, or just recover the desired instance from `BeanFactory`. – Luiggi Mendoza Apr 01 '14 at 16:37

3 Answers3

4

Similar to RC.'s answer, instead of using a Map and adding the values by you, just let the Spring BeanFactory handle this for you:

@Controller
@RequestMapping("/something")
public class SomeController {
    @Autowired
    private BeanFactory beanFactory;

    @RequestMapping("/doStuff")
    public void login(@RequestParam("type") String type) {
        SomeService aService = (SomeService)beanFactory.getBean(type);
        aService.doStuff();
    }
}
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • I ended up adopting this one, which is similar to the one from RC I previously tried with a service factory but I was gaining nothing compared with the `BeanFactory` approach and I was struggling against bean instantiation issues. Thanks all for the awesome answers, you did my day. – saste Apr 02 '14 at 14:53
1

You could use a map here, something along this:

@Controller
@RequestMapping("/something")
public class SomeController {
    @Autowired
    private SomeService someService;

    @Autowired
    private SomeOtherService someOtherService;

    // ...

    private final Map<String, ServiceCommonInterface> serviceMap = new HashMap<>();

    @PostConstruct
    private void postConstruct() {
        serviceMap.put(typeForSomeService, someService);
        serviceMap.put(typeForSomeOtherService, someOtherService);
    }

    @RequestMapping("/doStuff")
    public void login(@RequestParam("type") String type) {
        // TODO: ensure type is correct (an enum might be handy here)
        serviceMap.get(type).doStuff();
    }
}

Or better, as stated in comments you can leverage qualifiers:

@Controller
@RequestMapping("/something")
public class SomeController {
    @Autowired
    private ApplicationContext applicationContext;

    @RequestMapping("/doStuff")
    public void login(@RequestParam("type") String type) {
        // TODO: ensure type is a correct bean name
        applicationContext.getBean(type, ServiceCommonInterface.class).doStuff();
    }
}
  • That's what I was going for. Alternatively, instead of a direct map, create a bean where you inject all the services and generate a `Map`. – Sotirios Delimanolis Apr 01 '14 at 16:39
  • yeah I was reading you comment and thinking "damn I can just use a qualifier", the map being the spring context –  Apr 01 '14 at 16:39
0

Depending on the number of types you wish to support there are two options I see.

1) Autowire in a factory as you mentioned and lazily create each service as needed. If the services are stateless you could keep a reference to the object after created so would only need to create once per type.

2) Autowire in a Spring Map with the key being your types and the value being the correct service to use. Then when your receive the type, can retrieve the correct service impl for your map.

For example for map: http://www.mkyong.com/spring/spring-collections-list-set-map-and-properties-example/ or see How to inject a Map<String, List> in java springs?

Both of these require you to create an interface for your service which is something you said is possible.

Community
  • 1
  • 1
astropcr
  • 177
  • 2
  • 10