Карта перечислений и внедрение зависимостей в Spring 2.5

Предположим, у нас есть следующий код Java:

public class Maintainer {
   private Map<Enum, List<Listener>> map;

   public Maintainer() {
      this.map = new java.util.ConcurrentHashMap<Enum, List<Listener>>();
   }

   public void addListener( Listener listener, Enum eventType ) {
      List<Listener> listeners;
      if ( ( listeners = map.get( eventType ) ) == null ) {
         listeners = new java.util.concurrent.CopyOnWriteArrayList<Listener>();
         map.put( eventType, listeners );
      }
      listeners.add( listener );
   }
}

Этот фрагмент кода представляет собой не что иное, как немного улучшенный шаблон слушателя, где каждый слушатель сообщает, какой тип события ему интересен, а предоставленный метод поддерживает параллельную карту этих отношений.

Изначально я хотел, чтобы этот метод вызывался через мою собственную структуру аннотаций, но натолкнулся на кирпичную стену с различными ограничениями аннотации (например, у вас не может быть java.lang.Enum в качестве параметра аннотации, также есть набор различных проблем с загрузчиком классов), поэтому решил использовать Spring.

Может ли кто-нибудь сказать мне, как это сделать Spring_ify_? Я хочу достичь:
. 1. Определите класс Сопровождающий как компонент Spring. 2. Сделайте так, чтобы все виды слушателей могли регистрироваться в Сопровождающий через XML, используя метод addListener. Документы Spring и Google не очень щедры на примеры.

Есть ли способ легко этого добиться?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
6 573
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Что было бы неправильным в том, чтобы сделать что-то вроде следующего:

Определение интерфейса Maintainer с помощью метода addListener (Listener, Enum).

Создайте класс DefaultMaintainer (как указано выше), который реализует Maintainer.

Затем в каждом классе Listener «вставьте» интерфейс Maintainer (внедрение конструктора может быть хорошим выбором). Затем слушатель может зарегистрироваться в Сопровождающем.

кроме этого, я не на 100% понимаю, в чем именно заключаются ваши трудности со Spring на данный момент! :)

You said "... you can't have java.lang.Enum as" annotation param ..."

Думаю, в этом вы ошибаетесь. Недавно я использовал в проекте что-то вроде этого:

public @interface MyAnnotation {
    MyEnum value();
}

Что ж, это именно то, что я сказал - «у вас не может быть java.lang.Enum в качестве параметра аннотации». Я спрашивал, как мне это сделать весной?

mindas 16.09.2008 19:26

1) Define Maintainer class as a Spring bean.

Применяется стандартный синтаксис Spring:

<bean id = "maintainer" class = "com.example.Maintainer"/>

2) Make it so that all sorts of listeners would be able to register themselves to Maintainer via XML by using addListener method. Spring doc nor Google are very generous in examples.

Это сложнее. Вы мог используете MethodInvokingFactoryBean для индивидуального вызова maintainer#addListener, например:

<bean id = "listener" class = "com.example.Listener"/>

<bean id = "maintainer.addListener" class = "org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name = "targetObject" ref = "maintainer"/>
  <property name = "targetMethod" value = "addListener"/>
  <property name = "arguments">
    <list>
      <ref>listener</ref>
      <value>com.example.MyEnum</value>
   </list>
 </property>
</bean>

Однако это громоздко и потенциально подвержено ошибкам. Я попытался сделать что-то подобное в проекте и вместо этого создал служебный класс Spring. На данный момент у меня нет исходного кода, поэтому я опишу, как реализовать то, что я сделал.

1) Реорганизуйте типы событий, которые прослушиваются, в интерфейс MyListener.

public interface MyListener extends Listener {
  public Enum[] getEventTypes()
}

Что меняет метод регистрации на

public void addListener(MyListener listener)

2) Создайте вспомогательный класс Spring, который находит всех соответствующих слушателей в контексте и вызывает сопровождающего # addListener для каждого найденного слушателя. Я бы начал с BeanFilteringSupport, а также реализовал BeanPostProcessor (или ApplicationListener) для регистрации bean-компонентов после того, как все bean-компоненты были созданы.

Всем спасибо за ответы. Во-первых, быстрое уточнение всех ответов. 1. (alexvictor) Да, в качестве параметра аннотации можно указать конкретный перечислить, но не java.lang.Enum.
2. Ответ flicken правильный, но, к сожалению, немного пугающий. Я не эксперт по Spring, но делаю это таким образом (создавая методы для более легкого доступа к Spring), это кажется излишним, как и решение MethodInvokingFactoryBean. Хотя хотел выразить искреннюю благодарность за ваше время и усилия. 3. Ответ Phill немного необычен (вместо того, чтобы внедрять bean-компонент слушателя, внедрить его сопровождающий!), Но, я считаю, самый чистый из всех доступных. Думаю, я пойду по этому пути.

Еще раз большое спасибо за вашу помощь.

Слегка оффтоп (поскольку речь идет не о Spring), но в вашей реализации AddListener есть состояние гонки:

  if ( ( listeners = map.get( eventType ) ) == null ) {
     listeners = new java.util.concurrent.CopyOnWriteArrayList<Listener>();
     map.put( eventType, listeners );
  }
  listeners.add( listener );

Если два потока вызывают этот метод одновременно (для типа события, у которого ранее не было слушателей), map.get (eventType) вернет значение null в обоих потоках, каждый поток создаст свой собственный CopyOnWriteArrayList (каждый из которых содержит одного слушателя), один поток заменит список, созданный другим, и первый слушатель будет забыт.

Чтобы исправить это, измените:

private Map<Enum, List<Listener>> map;

...

map.put( eventType, listeners );

к:

private ConcurrentMap<Enum, List<Listener>> map;

...

map.putIfAbsent( eventType, listeners );
listeners = map.get( eventType );

Другие вопросы по теме