Spring 2.5中的枚举映射和依赖项注入

时间:2020-03-05 18:55:42  来源:igfitidea点击:

假设我们有以下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.做到这一点,以便各种侦听器都可以使用addListener方法通过XML将自己注册到Maintenanceer。 Spring doc和Google都非常慷慨。

有没有一种方法可以轻松实现这一目标?

解决方案

回答

进行如下操作可能会出问题:

使用addListener(Listener,Enum)方法定义" Maintainer"接口。

创建一个实现维护者的DefaultMaintainer类(如上所述)。

然后,在每个侦听器类中,"注入" Maintainer接口(构造函数注入可能是一个不错的选择)。然后,侦听器可以向维护者注册自己。

除此之外,我目前还不清楚我们目前在Spring遇到的困难是100%确切的! :)

回答

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

我认为你在这方面是错的。我最近在一个项目中使用了这样的东西:

public @interface MyAnnotation {
    MyEnum value();
}

回答

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帮助程序类,该类在上下文中查找所有相关的侦听器,并为找到的每个侦听器调用maintenanceer#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 );