Question

Supposons que nous ayons le code Java suivant:

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 );
   }
}

Cet extrait de code n’est rien d’autre qu'un modèle d’auditeur légèrement amélioré, dans lequel chaque auditeur indique le type d’événement qui l’intéresse, et la méthode fournie maintient une mappe simultanée de ces relations.

Au départ, je voulais que cette méthode soit appelée via mon propre cadre d'annotation, mais je me suis heurtée à un mur de briques comportant diverses limitations pour les annotations (par exemple, vous ne pouvez pas utiliser java.lang.Enum comme paramètre d'annotation. , il existe également un ensemble de problèmes de chargeur de classe) qui ont donc décidé d’utiliser Spring.

Quelqu'un pourrait-il me dire comment puis-je Spring_ify_ this? Ce que je veux accomplir est:
1. Définissez la classe Maintainer en tant que bean Spring.
2. Faites en sorte que toutes sortes d'auditeurs puissent s'inscrire eux-mêmes auprès de Maintainer via XML en utilisant la méthode addListener . Les documents de printemps et Google ne sont pas très généreux en exemples.

Y a-t-il un moyen d'y parvenir facilement?

Était-ce utile?

La solution

Quel problème y aurait-il à faire quelque chose comme ce qui suit:

Définition d'une interface 'Maintainer' avec la méthode addListener (Listener, Enum).

Créez une classe DefaultMaintainer (comme ci-dessus) qui implémente Maintainer.

Ensuite, dans chaque classe d'écouteur, injectez l'interface Maintainer (l'injection de constructeur pourrait être un bon choix). L’auditeur peut alors s’inscrire lui-même auprès du mainteneur.

À part ça, je ne suis pas sûr à 100% de votre problème avec Spring en ce moment! :)

Autres conseils

Légèrement hors-sujet (car il ne s'agit pas de Spring), mais votre implémentation de AddListener est dans une situation critique.

  if( ( listeners = map.get( eventType ) ) == null ) {
     listeners = new java.util.concurrent.CopyOnWriteArrayList<Listener>();
     map.put( eventType, listeners );
  }
  listeners.add( listener );

Si deux threads appellent cette méthode en même temps (pour un type d'événement qui n'avait auparavant aucun écouteur), map.get (eventType) renverra null dans les deux threads, chaque thread créera son propre CopyOnWriteArrayList (chacun contenant un seul écouteur), un fil remplacera la liste créée par l’autre et le premier écouteur sera oublié.

Pour résoudre ce problème, changez:

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) Définissez la classe Maintainer en tant que bean Spring.

La syntaxe Spring standard s'applique:

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

2) Faire en sorte que toutes sortes d'auditeurs puissent s'inscrire eux-mêmes auprès de Maintainer via XML en utilisant la méthode addListener. Les documents de printemps et Google ne sont pas très généreux en exemples.

C'est plus compliqué. Vous pouvez utiliser MethodInvokingFactoryBean pour appeler individuellement mainteneur # addListener , comme suit:

<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>

Cependant, ceci est lourd et potentiellement sujet aux erreurs. J'ai essayé quelque chose de similaire sur un projet et créé une classe utilitaire Spring pour aider à la place. Je n'ai pas le code source disponible pour le moment, je vais donc décrire comment mettre en œuvre ce que j'ai fait.

1) Refactoriser les types d'événements écoutés dans une interface MyListener

public interface MyListener extends Listener {
  public Enum[] getEventTypes()
}

Ce qui change la méthode d'enregistrement en

public void addListener(MyListener listener)

2) Créez une classe Spring helper qui trouve tous les écouteurs pertinents dans le contexte et appelle le responsable # addListener pour chaque auditeur trouvé. Je commencerais par BeanFilteringSupport et implémenterais également BeanPostProcessor (ou ApplicationListener ) pour enregistrer les beans après instanciation de tous les beans.

  

Vous avez dit "... vous ne pouvez pas utiliser java.lang.Enum comme" "   paramètre d'annotation ... "

Je pense que vous vous trompez à ce sujet. J'ai récemment utilisé sur un projet quelque chose comme ceci:

public @interface MyAnnotation {
    MyEnum value();
}

Merci à tous pour les réponses. Premièrement, un suivi rapide de toutes les réponses.
1. (alexvictor) Oui, vous pouvez utiliser enum comme paramètre d'annotation concret, mais pas java.lang.Enum .
2. La réponse fournie par flicken est correcte, mais malheureusement un peu effrayante. Je ne suis pas un expert de Spring, mais en procédant ainsi (en créant des méthodes pour faciliter l’accès à Spring), cela semble un peu exagéré, tout comme la solution MethodInvokingFactoryBean . Bien que je veuille exprimer mes sincères remerciements pour votre temps et vos efforts.
3. La réponse de Phill est un peu inhabituelle (au lieu d'injecter du haricot d'auditeur, injectez son mainteneur!), Mais, je crois, la plus propre qui soit. Je pense que je vais aller dans cette voie.

Encore une fois, un grand merci pour votre aide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top