Карта Enums и внедрения зависимостей в 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 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.Ответ Фила немного необычен (вместо внедрения компонента-слушателя, внедрите его сопровождающего!), но, я считаю, самый чистый из всех доступных.Думаю, я пойду по этому пути.
Еще раз большое спасибо вам за помощь.