3

假设我们有以下 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注册到Maintainer 。Spring doc 和 Google 在示例中都非常慷慨。

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

4

5 回答 5

3

执行以下操作会有什么问题:

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

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

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

除此之外,我还不是 100% 清楚你目前在 Spring 上的困难是什么!:)

于 2008-09-16T11:51:23.773 回答
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 );
于 2008-09-18T15:13:51.167 回答
1

1)将Maintainer类定义为Spring bean。

标准 Spring 语法适用:

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

2) 使各种监听器都能够使用 addListener 方法通过 XML 将自己注册到维护者。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(或)注册 bean。ApplicationListener

于 2008-09-16T18:05:30.113 回答
0

你说“......你不能将 java.lang.Enum 作为”注解参数......”

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

public @interface MyAnnotation {
    MyEnum value();
}
于 2008-09-16T14:46:54.867 回答
0

谢谢大家的回答。首先,快速跟进所有答案。
1. (alexvictor) 是的,您可以将具体枚举作为注释参数,但不能使用java.lang.Enum
2. flicken提供的答案是正确的,可惜有点吓人。我不是 Spring 专家,但以这种方式做事(创建方法以便于 Spring 访问)这似乎有点矫枉过正,MethodInvokingFactoryBean解决方案也是如此。尽管我想对您的时间和精力表示衷心的感谢。
3. Phill 的回答有点不寻常(不是注入侦听器 bean,而是注入它的维护者!),但我相信,这是所有可用的最干净的。我想我会沿着这条路走下去。

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

于 2008-09-17T09:48:20.753 回答