Eventos da Primavera

Eventos da Primavera

1. Visão geral

Neste artigo, discutiremoshow to use events in Spring.

Os eventos são uma das funcionalidades mais negligenciadas na estrutura, mas também uma das mais úteis. E - como muitas outras coisas no Spring - a publicação de eventos é um dos recursos fornecidos porApplicationContext.

Existem algumas diretrizes simples a seguir:

  • o evento deve estenderApplicationEvent

  • o editor deve injetar um objetoApplicationEventPublisher

  • o ouvinte deve implementar a interfaceApplicationListener

2. Um Evento Personalizado

O Spring permite criar e publicar eventos personalizados que - por padrão -are synchronous. Isso tem algumas vantagens - como, por exemplo, o ouvinte poder participar do contexto de transação do editor.

2.1. Um evento de aplicativo simples

Vamos criara simple event class - apenas um espaço reservado para armazenar os dados do evento. Nesse caso, a classe de eventos mantém uma mensagem String:

public class CustomSpringEvent extends ApplicationEvent {
    private String message;

    public CustomSpringEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
}

2.2. Um editor

Agora vamos criara publisher of that event. O editor constrói o objeto de evento e o publica para qualquer pessoa que estiver ouvindo.

Para publicar o evento, o editor pode simplesmente injetarApplicationEventPublishere usar a APIpublishEvent():

@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void doStuffAndPublishAnEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }
}

Como alternativa, a classe do editor pode implementar a interfaceApplicationEventPublisherAware - isso também injetará o editor do evento na inicialização do aplicativo. Normalmente, é mais simples apenas injetar no editor@Autowire.

2.3. Um ouvinte

Finalmente, vamos criar o ouvinte.

O único requisito para o ouvinte é ser um bean e implementar a interfaceApplicationListener:

@Component
public class CustomSpringEventListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(CustomSpringEvent event) {
        System.out.println("Received spring custom event - " + event.getMessage());
    }
}

Observe como nosso ouvinte personalizado é parametrizado com o tipo genérico de evento personalizado - o que torna o tipo de métodoonApplicationEvent() seguro. Isso também evita a necessidade de verificar se o objeto é uma instância de uma classe de evento específica e a sua conversão.

E, como já discutido - por padrãospring events are synchronous - o métododoStuffAndPublishAnEvent() bloqueia até que todos os ouvintes terminem de processar o evento.

3. Criação de eventos assíncronos

Em alguns casos, publicar eventos de forma síncrona não é realmente o que estamos procurando -we may need async handling of our events.

Você pode ativar isso na configuração criando um beanApplicationEventMulticaster com um executor; para nossos propósitos,SimpleAsyncTaskExecutor funciona bem:

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster =
          new SimpleApplicationEventMulticaster();

        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

As implementações do evento, do editor e do ouvinte permanecem as mesmas de antes - mas agora,the listener will asynchronously deal with the event in a separate thread.

4. Eventos de estrutura existentes

A própria Spring publica uma variedade de eventos prontos para uso. Por exemplo, oApplicationContext irá disparar vários eventos de estrutura. E.g. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent etc.

Esses eventos fornecem aos desenvolvedores de aplicativos uma opção para se conectar ao ciclo de vida do aplicativo e ao contexto e adicionar sua própria lógica personalizada, quando necessário.

Aqui está um exemplo rápido de um ouvinte ouvindo atualizações de contexto:

public class ContextRefreshedListener
  implements ApplicationListener {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context re-freshed event. ");
    }
}

Para saber mais sobre os eventos de estrutura existentes, dê uma olhada emour next tutorial here.

5. Listener de eventos orientado por anotações

A partir do Spring 4.2, um ouvinte de evento não precisa ser um bean implementando a interfaceApplicationListener - ele pode ser registrado em qualquer métodopublic de um bean gerenciado por meio da anotação@EventListener:

@Component
public class AnnotationDrivenContextStartedListener {
    // @Async
    @EventListener
    public void handleContextStart(ContextStartedEvent cse) {
        System.out.println("Handling context started event.");
    }
}

Como antes, a assinatura do método declara o tipo de evento que consome. Como antes, esse ouvinte é chamado de forma síncrona. Mas agora torná-lo assíncrono é tão simples quanto adicionar uma anotação@Async (não se esqueça deenable Async support no aplicativo).

6. Suporte de genéricos

Também é possível despachar eventos com informações genéricas no tipo de evento.

6.1. Um evento de aplicativo genérico

Let’s create a generic event type. Em nosso exemplo, a classe de evento contém qualquer conteúdo e um indicador de statussuccess:

public class GenericSpringEvent {
    private T what;
    protected boolean success;

    public GenericSpringEvent(T what, boolean success) {
        this.what = what;
        this.success = success;
    }
    // ... standard getters
}

Observe a diferença entreGenericSpringEvent eCustomSpringEvent. Agora temos a flexibilidade de publicar qualquer evento arbitrário e não é mais necessário estender a partir deApplicationEvent.

6.2. Um ouvinte

Agora vamos criara listener of that event. Poderíamos definir o ouvinte implementando a interfacethe ApplicationListener como antes:

@Component
public class GenericSpringEventListener
  implements ApplicationListener> {
    @Override
    public void onApplicationEvent(@NonNull GenericSpringEvent event) {
        System.out.println("Received spring generic event - " + event.getWhat());
    }
}

Mas, infelizmente, essa definição exige que herdemosGenericSpringEvent da classeApplicationEvent. Portanto, para este tutorial, vamos usar um ouvinte de eventos baseado em anotações discutidopreviously.

Também é possívelmake the event listener conditional definindo uma expressão booleana SpEL na anotação@EventListener. Nesse caso, o manipulador de eventos só será invocado paraGenericSpringEvent deString bem-sucedido:

@Component
public class AnnotationDrivenEventListener {
    @EventListener(condition = "#event.success")
    public void handleSuccessful(GenericSpringEvent event) {
        System.out.println("Handling generic event (conditional).");
    }
}

OSpring Expression Language (SpEL) é uma linguagem de expressão poderosa que é abordada em detalhes em outro tutorial.

6.3. Um editor

O editor do evento é semelhante ao descritoabove. Porém, devido ao apagamento do tipo, precisamos publicar um evento que resolva o parâmetro genérico no qual filtraríamos. Por exemplo,class GenericStringSpringEvent extends GenericSpringEvent<String>.

E háan alternative way of publishing events. Se retornarmos um valor não nulo de um método anotado com@EventListener como resultado, o Spring Framework enviará esse resultado como um novo evento para nós. Além disso, podemos publicar vários novos eventos retornando-os em uma coleção como resultado do processamento de eventos.

7. Eventos de transação vinculados

Este parágrafo é sobre como usar a anotação@TransactionalEventListener. Para saber mais sobre o gerenciamento de transações, confira o tutorialTransactions with Spring and JPA.

Desde o Spring 4.2, o framework fornece uma nova anotação@TransactionalEventListener, que é uma extensão de@EventListener, que permite vincular o ouvinte de um evento a uma fase da transação. A ligação é possível nas seguintes fases da transação:

  • AFTER_COMMIT (padrão) é usado para disparar o evento se a transação tivercompleted successfully

  • AFTER_ROLLBACK - se a transação tiverrolled back

  • AFTER_COMPLETION - se a transação tivercompleted (um alias paraAFTER_COMMITeAFTER_ROLLBACK)

  • BEFORE_COMMIT é usado para disparar o evento certobefore transaçãocommit

Aqui está um exemplo rápido de ouvinte de evento transacional:

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleCustom(CustomSpringEvent event) {
    System.out.println("Handling event inside a transaction BEFORE COMMIT.");
}

Este ouvinte será invocado apenas se houver uma transação na qual o produtor do evento está sendo executado e está prestes a ser confirmada.

E, se nenhuma transação estiver em execução, o evento não será enviado, a menos que substituamos isso configurando o atributofallbackExecution paratrue.

8. Conclusão

Neste tutorial rápido, examinamos o básico dedealing with events in Spring - criar um evento personalizado simples, publicá-lo e, em seguida, tratá-lo em um ouvinte.

Também vimos como habilitar o processamento assíncrono de eventos na configuração.

Em seguida, aprendemos sobre as melhorias introduzidas no Spring 4.2, como ouvintes orientados a anotações, melhor suporte a genéricos e vinculação de eventos às fases da transação.

Como sempre, o código apresentado neste artigo está disponívelover on Github. Este é um projeto baseado em Maven, portanto, deve ser fácil importar e executar como está.