假设我们有以下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.将 Maintainer 类定义为Spring bean
2.通过使用 addListener 方法,使各种各样的侦听器能够通过XML将自己注册到 Maintainer 。 Spring doc和Google在例子中非常慷慨。
有没有办法轻松实现这一目标?
答案 0 :(得分:2)
执行以下操作会出现什么问题:
使用addListener(Listener,Enum)方法定义'Maintainer'接口。
创建一个实现Maintainer的DefaultMaintainer类(如上所述)。
然后,在每个Listener类中,“注入”Maintainer接口(构造函数注入可能是一个不错的选择)。然后,监听器可以向Maintainer注册自己。
除此之外,我并不是100%清楚你目前对Spring的困难! :)答案 1 :(得分:2)
稍微偏离主题(因为这不是关于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 );
答案 2 :(得分:1)
1)将Maintainer类定义为Spring bean。
标准Spring语法适用:
<bean id="maintainer" class="com.example.Maintainer"/>
2)通过使用addListener方法,使各种各样的侦听器能够通过XML将自己注册到Maintainer。 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辅助类,在上下文中查找所有相关的侦听器,并为找到的每个侦听器调用maintainer#addListener。我将从BeanFilteringSupport
开始,并在实例化所有bean之后实现BeanPostProcessor
(或ApplicationListener
)来注册bean。
答案 3 :(得分:0)
你说“......你不能把java.lang.Enum作为” 注释参数......“
我认为你错了。我最近在项目中使用了这样的东西:
public @interface MyAnnotation {
MyEnum value();
}
答案 4 :(得分:0)
谢谢大家的答案。首先,快速跟进所有答案
1.(alexvictor)是的,你可以将具体的 enum 作为注释参数,但不能 java.lang.Enum 。
2. flicken提供的答案是正确的,但遗憾的是有点可怕。我不是Spring专家,而是以这种方式做事(创建更容易Spring访问的方法)这似乎有点矫枉过正,就像 MethodInvokingFactoryBean 解决方案一样。虽然我想对你的时间和精力表示衷心的感谢
Phill的答案有点不寻常(而不是注入监听器bean,注入其维护者!),但我相信,这是最干净的。我想我会走这条路。
再次,非常感谢你的帮助。