Pergunta

Vamos supor que temos o seguinte código 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 );
   }
}

Este trecho de código nada mais é do que um padrão de ouvinte um pouco melhorado, onde cada ouvinte informa em que tipo de evento está interessado e o método fornecido mantém um mapa simultâneo desses relacionamentos.

Inicialmente, eu queria que esse método fosse chamado por meio de minha própria estrutura de anotação, mas me deparei com uma parede de tijolos com várias limitações de anotação (por exemplo,você não pode ter java.lang.Enum como parâmetro de anotação, também há um conjunto de vários problemas do carregador de classe), portanto decidi usar o Spring.

Alguém poderia me dizer como faço para Spring_ify_ isso?O que eu quero alcançar é:
1.Definir Mantenedor classe como um feijão Spring.
2.Faça com que todos os tipos de ouvintes possam se registrar no Mantenedor via XML usando adicionarListener método.Spring doc nem Google são muito generosos nos exemplos.

Existe uma maneira de conseguir isso facilmente?

Foi útil?

Solução

O que haveria de errado em fazer algo como o seguinte:

Definindo uma interface 'Maintainer' com o método addListener(Listener, Enum).

Crie uma classe DefaultMaintainer (como acima) que implementa o Mantenedor.

Então, em cada classe Listener, 'injete' a interface do Mantenedor (a injeção do construtor pode ser uma boa escolha).O ouvinte pode então registrar-se no Mantenedor.

fora isso, não tenho 100% de certeza sobre qual é exatamente a sua dificuldade com o Spring no momento!:)

Outras dicas

Um pouco offtopic (já que não se trata do Spring), mas há uma condição de corrida na sua implementação do AddListener:

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

Se dois threads chamarem esse método ao mesmo tempo (para um tipo de evento que anteriormente não tinha ouvintes), map.get( eventType ) retornará nulo em ambos os threads, cada thread criará seu próprio CopyOnWriteArrayList (cada um contendo um único ouvinte), um thread substituirá a lista criada pelo outro e o primeiro ouvinte será esquecido.

Para corrigir isso, altere:

private Map<Enum, List<Listener>> map;

...

map.put( eventType, listeners );

para:

private ConcurrentMap<Enum, List<Listener>> map;

...

map.putIfAbsent( eventType, listeners );
listeners = map.get( eventType );

1) Defina a classe Mantenedor como um bean Spring.

A sintaxe padrão do Spring se aplica:

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

2) Faça com que todos os tipos de ouvintes possam se registrar no Mantenedor via XML usando o método addListener.Spring doc nem Google são muito generosos nos exemplos.

Isso é mais complicado.Você poderia usar MethodInvokingFactoryBean ligar individualmente maintainer#addListener, igual a:

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

No entanto, isso é complicado e potencialmente sujeito a erros.Tentei algo semelhante em um projeto e criei uma classe de utilitário Spring para ajudar.Não tenho o código-fonte disponível no momento, então descreverei como implementar o que fiz.

1) Refatore os tipos de eventos ouvidos em um MyListener interface

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

O que muda o método de registro para

public void addListener(MyListener listener)

2) Crie uma classe auxiliar Spring que encontre todos os ouvintes relevantes no contexto e chame o mantenedor#addListener para cada ouvinte encontrado.eu começaria com BeanFilteringSupport, e também implementar BeanPostProcessor (ou ApplicationListener) para registrar os beans depois que todos os beans tiverem sido instanciados.

Você disse "...você não pode ter java.lang.Enum como" anotação param..."

Eu acho que você está errado nisso.Recentemente usei em um projeto algo assim:

public @interface MyAnnotation {
    MyEnum value();
}

Obrigado a todos pelas respostas.Primeiro, um rápido acompanhamento de todas as respostas.
1.(alexvictor) Sim, você pode ter concreto enumeração como parâmetro de anotação, mas não java.lang.Enum.
2.A resposta fornecida por flicken está correta, mas infelizmente é um pouco assustadora.Eu não sou um especialista em Spring, mas fazer as coisas dessa maneira (criando métodos para facilitar o acesso ao Spring) parece um pouco exagerado, assim como o MétodoInvocandoFactoryBean solução.Embora eu quisesse expressar meus sinceros agradecimentos por seu tempo e esforço.
3.A resposta de Phill é um pouco incomum (em vez de injetar o bean ouvinte, injete seu mantenedor!), mas acredito que seja a mais limpa de todas disponíveis.Acho que vou seguir esse caminho.

Mais uma vez, um grande obrigado pela sua ajuda.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top