Вопрос

Предположим, у нас есть следующий 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 bean.
2.Сделайте так, чтобы самые разные слушатели могли зарегистрироваться на Сопровождающий через XML с помощью добавить прослушиватель метод.Spring doc и Google не очень щедры на примеры.

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

Это было полезно?

Решение

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

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

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

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

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

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

Немного оффтоп (поскольку речь идет не о 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 );

1) Определите класс сопровождающего как компонент Spring.

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

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

2) Сделать так, чтобы все виды слушателей могли регистрироваться в Maintenanceer через XML, используя метод addListener.Spring doc и Google не очень щедры на примеры.

Это сложнее.Ты мог использовать 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, который находит всех соответствующих прослушивателей в контексте и вызывает Maintenanceer#addListener для каждого найденного прослушивателя.Я бы начал с BeanFilteringSupport, а также реализовать BeanPostProcessor (или ApplicationListener) для регистрации bean-компонентов после создания экземпляров всех bean-компонентов.

Вы сказали "...Вы не можете иметь java.lang.enum как "аннотационный парамет ..."

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

public @interface MyAnnotation {
    MyEnum value();
}

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top