봄 이벤트

1. 개요

이 기사에서는 Spring에서 이벤트를 사용하는 방법에 대해 논의 할 것 입니다.

이벤트는 프레임 워크에서 간과되는 기능 중 하나이지만 더 유용한 기능 중 하나입니다. 그리고 – Spring의 다른 많은 것들과 마찬가지로 – 이벤트 퍼블리싱은 ApplicationContext가 제공하는 기능 중 하나입니다 .

따라야 할 몇 가지 간단한 지침이 있습니다.

  • 이벤트는 ApplicationEvent 를 확장해야합니다.
  • 게시자는 ApplicationEventPublisher 개체를 삽입해야 합니다.
  • 리스너는 ApplicationListener 인터페이스를 구현해야합니다.

2. 커스텀 이벤트

Spring을 사용하면 기본적 으로 동기식 인 사용자 지정 이벤트를 만들고 게시 할 수 있습니다 . 예를 들어 리스너가 게시자의 트랜잭션 컨텍스트에 참여할 수 있다는 점과 같은 몇 가지 장점이 있습니다.

2.1. 간단한 애플리케이션 이벤트

이벤트 데이터를 저장할 자리 표시자인 간단한 이벤트 클래스를 만들어 보겠습니다 . 이 경우 이벤트 클래스는 문자열 메시지를 보유합니다.

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. 출판사

이제 해당 이벤트의 게시자를 만들어 보겠습니다 . 게시자는 이벤트 개체를 구성하고 듣고있는 모든 사람에게 게시합니다.

이벤트를 게시하기 위해 게시자는 ApplicationEventPublisher 를 삽입 하고 publishEvent () API를 사용하기 만하면 됩니다.

@Component public class CustomSpringEventPublisher { @Autowired private ApplicationEventPublisher applicationEventPublisher; public void publishCustomEvent(final String message) { System.out.println("Publishing custom event. "); CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message); applicationEventPublisher.publishEvent(customSpringEvent); } }

또는 게시자 클래스가 ApplicationEventPublisherAware 인터페이스를 구현할 수 있습니다. 이렇게하면 응용 프로그램 시작시 이벤트 게시자도 삽입됩니다. 일반적으로 게시자에게 @Autowire를 주입하는 것이 더 간단합니다 .

2.3. 경청자

마지막으로 리스너를 만들어 보겠습니다.

리스너에 대한 유일한 요구 사항은 Bean이되고 ApplicationListener 인터페이스를 구현하는 것입니다 .

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

사용자 정의 리스너가 일반 유형의 사용자 정의 이벤트로 매개 변수화되어 onApplicationEvent () 메소드를 유형 안전하게 만드는 방법에 주목하십시오 . 또한 객체가 특정 이벤트 클래스의 인스턴스인지 확인하고 캐스팅 할 필요가 없습니다.

그리고 이미 논의했듯이 기본적으로 스프링 이벤트는 동기식입니다 . doStuffAndPublishAnEvent () 메서드는 모든 리스너가 이벤트 처리를 완료 할 때까지 차단됩니다.

3. 비동기 이벤트 생성

어떤 경우에는 이벤트를 동 기적으로 게시하는 것이 실제로 우리가 찾고있는 것이 아닙니다 . 이벤트를 비동기 적으로 처리해야 할 수도 있습니다 .

실행기로 ApplicationEventMulticaster 빈을 생성하여 구성에서이를 켤 수 있습니다 . 우리의 목적을 위해 SimpleAsyncTaskExecutor 는 잘 작동합니다.

@Configuration public class AsynchronousSpringEventsConfig { @Bean(name = "applicationEventMulticaster") public ApplicationEventMulticaster simpleApplicationEventMulticaster() { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); return eventMulticaster; } }

이벤트, 게시자 및 리스너 구현은 이전과 동일하게 유지되지만 이제 리스너는 별도의 스레드에서 이벤트를 비동기 적으로 처리합니다 .

4. 기존 프레임 워크 이벤트

Spring 자체는 다양한 이벤트를 즉시 게시합니다. 예를 들어 ApplicationContext 는 다양한 프레임 워크 이벤트를 발생시킵니다. 예 : ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent

이러한 이벤트는 애플리케이션 개발자에게 애플리케이션 및 컨텍스트의 수명주기에 연결하고 필요한 경우 자체 사용자 지정 논리를 추가 할 수있는 옵션을 제공합니다.

다음은 컨텍스트 새로 고침을 수신하는 리스너의 빠른 예입니다.

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

기존 프레임 워크 이벤트에 대해 자세히 알아 보려면 여기에서 다음 자습서를 참조하십시오.

5. 주석 기반 이벤트 리스너

Spring 4.2부터 이벤트 리스너는 ApplicationListener 인터페이스를 구현하는 빈이 될 필요가 없습니다 . @EventListener 어노테이션을 통해 관리 빈의 모든 공용 메소드에 등록 할 수 있습니다 .

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

이전과 마찬가지로 메서드 서명은 사용하는 이벤트 유형을 선언합니다.

기본적으로 리스너는 동 기적으로 호출됩니다. 그러나 @Async 주석 을 추가하여 쉽게 비 동기화 할 수 있습니다 . 하지만 애플리케이션에서 비동기 지원 을 활성화하는 것을 기억해야합니다 .

6. 제네릭 지원

이벤트 유형의 제네릭 정보를 사용하여 이벤트를 전달할 수도 있습니다.

6.1. 일반 애플리케이션 이벤트

Let's create a generic event type. In our example, the event class holds any content and a success status indicator:

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

Notice the difference between GenericSpringEvent and CustomSpringEvent. We now have the flexibility to publish any arbitrary event and it's not required to extend from ApplicationEvent anymore.

6.2. A Listener

Now let’s create a listener of that event. We could define the listener by implementing the ApplicationListener interface like before:

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

But unfortunately, this definition requires us to inherit GenericSpringEvent from the ApplicationEvent class. So for this tutorial, let's make use of an annotation-driven event listener discussed previously.

It is also possible to make the event listener conditional by defining a boolean SpEL expression on the @EventListener annotation. In this case, the event handler will only be invoked for a successful GenericSpringEvent of String:

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

The Spring Expression Language (SpEL) is a powerful expression language that's covered in detail in another tutorial.

6.3. A Publisher

The event publisher is similar to the one described above. But due to type erasure, we need to publish an event that resolves the generics parameter we would filter on. For example, class GenericStringSpringEvent extends GenericSpringEvent.

And there's an alternative way of publishing events. If we return a non-null value from a method annotated with @EventListener as the result, Spring Framework will send that result as a new event for us. Moreover, we can publish multiple new events by returning them in a collection as the result of event processing.

7. Transaction Bound Events

This paragraph is about using the @TransactionalEventListener annotation. To learn more about transaction management check out the Transactions with Spring and JPA tutorial.

Since Spring 4.2, the framework provides a new @TransactionalEventListener annotation, which is an extension of @EventListener, that allows binding the listener of an event to a phase of the transaction. Binding is possible to the following transaction phases:

  • AFTER_COMMIT (default) is used to fire the event if the transaction has completed successfully
  • AFTER_ROLLBACK – if the transaction has rolled back
  • AFTER_COMPLETION – if the transaction has completed (an alias for AFTER_COMMIT and AFTER_ROLLBACK)
  • BEFORE_COMMIT is used to fire the event right before transaction commit

Here's a quick example of transactional event listener:

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

This listener will be invoked only if there's a transaction in which the event producer is running and it's about to be committed.

And, if no transaction is running the event isn’t sent at all unless we override this by setting fallbackExecution attribute to true.

8. Conclusion

이 빠른 자습서에서 우리는 Spring에서 이벤트처리 하는 기본 사항을 살펴 보았습니다 . 간단한 사용자 지정 이벤트를 만들고 게시 한 다음 리스너에서 처리하는 것입니다.

또한 구성에서 이벤트의 비동기 처리를 활성화하는 방법을 간략하게 살펴 보았습니다.

그런 다음 주석 구동 리스너, 더 나은 제네릭 지원 및 트랜잭션 단계에 대한 이벤트 바인딩과 같이 Spring 4.2에 도입 된 개선 사항에 대해 배웠습니다.

항상 그렇듯이이 기사에 제시된 코드는 Github에서 사용할 수 있습니다. 이것은 Maven 기반 프로젝트이므로 그대로 가져 와서 실행하기 쉽습니다.