Внедрить несколько бинов Spring, которые наследуются от общего интерфейса, в массив внутри службы - PullRequest
2 голосов
/ 29 февраля 2012

Я столкнулся с ситуацией, которая возникла в другом проекте, и я не совсем уверен, что лучше всего это сделать в Grails.Чтобы настроить его, это то, что я делаю в простом проекте Spring.

У меня есть два класса, которые наследуются от одного и того же интерфейса:

public interface BaseInterface {
    void doSomething();
}

public class Impl1 implements BaseInterface {
    public void doSomething(){
        System.out.println("doing impl 1");
    }
}

public class Impl2 implements BaseInterface {
    public void doSomething(){
        System.out.println("doing impl 2");
    }
}

Пока что довольно стандартный, яесть N бинов, которые я хочу вызвать последовательно, чтобы сделать работу(Пример явно тривиальный).Внутри другого Java-класса я могу затем применить магию, чтобы получить все бобы ( autowired ) в виде массива.

@Autowired(required=false)
    private BaseInterface[] theWorkers;

Это даст мне массив рабочих бинов, еслиЯ добавил их в контейнер компонентов в конфигурации.

Теперь я пытаюсь сделать то же самое в Grails.Та же формула не работает.Помещение части @Autowired в службу и создание Impl1 и Impl2 в пределах resources.groovy, похоже, не справляются с этой задачей.Поэтому мне интересно, каково лучшее решение:

1) Я упускаю что-то простое, что очень легко сделает эту работу.

2) Сделайте что-то похожее на то, что предлагает duffymo здесь .Я бы создал именованный бин в resources.groovy, который использовал бы собственную фабрику.Эта фабрика будет генерировать класс, который будет содержать все классы, реализующие определенный интерфейс.Я бы использовал что-то подобное предложению, чтобы получить службы / классы, соответствующие критериям, а затем сделать так, чтобы этот сервис позволял кому-то перебирать его подклассы для выполнения работы.

3) Создание именованного компонента для каждого из Impl# классы в resources.groovy, а затем просто использовать их разные имена и вводить их все в классы по отдельности.Эта опция на самом деле не масштабируется и не дает большого динамизма, но будет работать.

1 Ответ

6 голосов
/ 29 февраля 2012

Если вы получаете доступ к контексту приложения Spring, вы можете вызвать getBeansOfType, который возвращает все известные bean-компоненты, которые реализуют указанный интерфейс или расширяют указанный базовый класс.Поэтому я бы зарегистрировал каждый компонент в resources.groovy, но также и класс менеджера, который получает ссылку на контекст приложения и находит для вас реализации интерфейса.Вы сказали, что хотите вызывать их последовательно, поэтому вам следует также реализовать интерфейс * 1003. *

Вот класс менеджера (поместите его в src / groovy / в нужной папке для пакета и переименуйте его в любое другое место).Вы хотите):

package com.foo

import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware

class BaseInterfaceManager implements ApplicationContextAware, InitializingBean {

   ApplicationContext applicationContext

   List<BaseInterface> orderedImpls

   void afterPropertiesSet() {
      orderedImpls = applicationContext.getBeansOfType(BaseInterface).values().sort { it.order }
   }
}

Затем измените bean-компоненты, чтобы они реализовали Ordered:

import org.springframework.core.Ordered;

public class Impl1 implements BaseInterface, Ordered {
   public void doSomething(){
      System.out.println("doing impl 1");
   }

   public int getOrder() {
      return 42;
   }
}

и

import org.springframework.core.Ordered;

public class Impl2 implements BaseInterface, Ordered {
   public void doSomething(){
      System.out.println("doing impl 2");
   }

   public int getOrder() {
      return 666;
   }
}

Зарегистрируйте все три в resources.groovy(используйте любые имена бинов, которые вы хотите):

beans = {

   impl1(Impl1)
   impl2(Impl2)

   baseInterfaceManager(BaseInterfaceManager)
}

И затем вы можете добавить внедрение зависимостей в сервис или контроллер или что угодно для бина baseInterfaceManager и использовать его для циклического прохождения классов реализации по порядку:

class FooService {

   def baseInterfaceManager

   void someMethod() {
      for (impl in baseInterfaceManager.orderedImpls) {
         impl.doSomething()
      }
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...