10

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.

E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.

Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?

DeejUK
  • 12,891
  • 19
  • 89
  • 169
  • A solution suggested by a colleague was that `@Autowired Map` will populate with bean ID as the key - perhaps that might be the least-worst solution. – DeejUK Jul 17 '13 at 16:14
  • Take a look at the similar post. http://stackoverflow.com/a/24525715/3796723 – linusdsunil Jul 02 '14 at 08:42

4 Answers4

14

We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:

@Component
public class ImTheClient{

    @Autowired
    private ImTheFactory factory;

    public void doSomething(
            Parameters parameters) throws Exception{        
        IWantThis theInstance = factory.getInstance(parameters);        

    }

}

So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:

@Component
public class ImTheFactoryImpl implements
        ImTheFactory {

    @Autowired
    private IWantThisBadly anInstance;
    @Autowired
    private IAlsoWantThis anotherInstance;

    @Override
    public IWantThis getInstance(Parameters parameters) {
        if (parameters.equals(Parameters.THIS)) {
            return anInstance;
        }

        if (parameters.equals(Parameters.THAT)) {
            return anotherInstance;
        }

        return null;
    }
}

So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.

Carlos Gavidia-Calderon
  • 7,145
  • 9
  • 34
  • 59
2

Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.

artbristol
  • 32,010
  • 5
  • 70
  • 103
2

Seems like do you want a ServiceLocator using the application context as registry.

See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.

Other option is to use a naming convention or annotation based configuration.

for example, assuming that you annotate Services with @ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.

public class AnnotationServiceLocator implements ServiceLocator {

    @Autowired
    private ApplicationContext context;
    private Map<String, Service> services;

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

    private void checkServices() {
        if (services == null) {
            services = new HashMap<String, Service>();
            Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
            for (Object bean : beans.values()) {
                ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
                services.put(ann.value(), (Service) bean);
            }
        }
    }   
}
Jose Luis Martin
  • 10,459
  • 1
  • 37
  • 38
-1

If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.

public class BusinessLogic {

  private BaseClassOfBeanAandB bean;

  public void methodCalledByUserAorB() {
    bean.doFoo();
  }

}
Manuel
  • 3,828
  • 6
  • 33
  • 48
  • Thanks. There are no HTTP sessions in this case, and the beans are too heavyweight to instantiate per-request. – DeejUK Jul 17 '13 at 14:12
  • Then how can you differ between user A and B? Based on the parameter passed to the method? – Manuel Jul 17 '13 at 14:13
  • Yep. There is some pre-authentication, so we get user IDs passed to us (actually several parameters that combine to create an identity, but that's beside the point). – DeejUK Jul 17 '13 at 14:17
  • Thanks for the downvote without cause. The OP did not state that there are no HTTP sessions, so how should I know my answer was not correct up to that point? – Manuel Jul 18 '13 at 08:03