Как выбрать экземпляр Spring bean во время выполнения

На основе параметров, переданных методу, мне нужно выбрать один из многих Spring beans, которые являются реализациями одного и того же класса, но настроены с различными параметрами.

например. если пользователь A вызывает метод, мне нужно вызвать dooFoo() на bean A, но если он пользователь B, тогда мне нужно вызвать тот же самый метод, только на bean B.

Есть ли способ 'Springier' сделать это иначе, чем придерживаться всего beans на карте и вывести ключ из параметров, переданных моему методу?

Ответ 1

Похоже, вы хотите ServiceLocator использовать контекст приложения как реестр.

См. ServiceLocatorFactoryBean класс поддержки для создания ключей сопоставления ServiceLocators с именами bean без связывания кода клиента с Spring.

Другим вариантом является использование соглашения об именах или конфигурации на основе аннотаций.

например, предполагая, что вы аннотируете Сервисы с помощью @ExampleAnnotation("someId"), вы можете использовать что-то вроде следующего Service Locator для их извлечения.

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);
            }
        }
    }   
}

Ответ 2

Мы сталкиваемся с этой проблемой в нашем проекте и решаем ее с помощью класса Factory -Like. Класс клиента - тот, который нуждался в bean во время выполнения - имел экземпляр factory, который был введен через Spring:

@Component
public class ImTheClient{

    @Autowired
    private ImTheFactory factory;

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

    }

}

Итак, экземпляр IWantThis зависит от значения времени выполнения параметра parameters. Реализация Factory выполняется следующим образом:

@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;
    }
}

Таким образом, экземпляр Factory содержит ссылку на оба возможных значения класса IWantThis, будучи IWantThisBadly и IAlsoWantThis обе реализации IWantThis.

Ответ 3

Приклеивание их на карте звучит отлично. Если это Spring -управленное отображение (с использованием util:map или в конфигурации Java), это лучше, чем создание его где-то еще, потому что тогда Spring владеет всеми объектными ссылками и может правильно управлять своим жизненным циклом.

Ответ 4

Если beans (A, B), о котором вы говорите, SessionScope, это не проблема, они будут правильно выбраны.

public class BusinessLogic {

  private BaseClassOfBeanAandB bean;

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

}