Spring 2.5中Enums和依赖注入的映射

时间:2008-09-16 11:44:29

标签: java spring enums maps

假设我们有以下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在例子中非常慷慨。

有没有办法轻松实现这一目标?

5 个答案:

答案 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,注入其维护者!),但我相信,这是最干净的。我想我会走这条路。

再次,非常感谢你的帮助。