原文: https://howtodoinjava.com/spring-core/how-to-publish-and-listen-application-events-in-spring/

了解在 Spring 应用程序中创建基于发布者-订阅者的事件。 Spring 创建应用程序事件,发布它们然后在事件处理器中监听提供了内置支持。 要创建/监听应用程序事件,需要遵循一些简单的准则:

  1. 事件应该扩展ApplicationEvent
  2. 发布者应该注入ApplicationEventPublisher对象
  3. 监听器应该实现ApplicationListener接口

1. 为什么是 Spring 事件

有时在 Spring 应用程序中,您可能需要添加用于监听特定应用程序事件的功能,以便可以根据应用程序逻辑处理这些事件。

这些事件的示例可以是添加/删除员工时; 或某种事务完成或回滚。 您始终可以将事件处理代码编写为现有应用程序代码中的另一种方法,但是它会与您现有的代码紧密结合,并且您以后将没有太多处理方法来更改它(假设您不想为这些事件处理这些事件)。 一定的时间)。

如果可以通过应用程序上下文文件配置事件处理器,则在大多数情况下,您无需更改应用程序代码以及事件处理器代码。 任何时候您需要关闭事件处理; 或者,您想要为该事件添加另一个事件处理器,那么只需更改上下文文件的配置即可。 听起来不错 !!

在基于 Spring 事件的通信模型中,发送方组件仅发布事件,而不知道谁将是。 实际上,可能有多个接收器组件。 同样,接收者也不必知道谁在发布事件。 监听器可以同时监听来自不同发送者的多个事件。 这样,发送方和接收方组件松散耦合。

让我们学习,如何在您的 Spring 应用程序中实现此发布和监听事件。

2. 发布和监听 Spring 应用程序事件

2.1. 创建自定义应用程序事件类

所有事件类都必须扩展ApplicationEvent类。

EmployeeEvent.java

  1. public class EmployeeEvent extends ApplicationEvent
  2. {
  3. private static final long serialVersionUID = 1L;
  4. private String eventType;
  5. private EmployeeDTO employee;
  6. //Constructor's first parameter must be source
  7. public EmployeeEvent(Object source, String eventType, EmployeeDTO employee)
  8. {
  9. //Calling this super class constructor is necessary
  10. super(source);
  11. this.eventType = eventType;
  12. this.employee = employee;
  13. }
  14. public String getEventType() {
  15. return eventType;
  16. }
  17. public EmployeeDTO getEmployee() {
  18. return employee;
  19. }
  20. }

2.2. 发布者 – 实现ApplicationEventPublisherAware接口

任何实现ApplicationEventPublisherAware接口的 bean,都可以使用其publishEvent()方法将事件发送给监听器。

EmployeeManagerImpl.java

  1. @Service ("employeeManager")
  2. public class EmployeeManagerImpl implements EmployeeManager, ApplicationEventPublisherAware
  3. {
  4. @Autowired
  5. private EmployeeDAO dao;
  6. private ApplicationEventPublisher publisher;
  7. //You must override this method; It will give you acces to ApplicationEventPublisher
  8. public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
  9. this.publisher = publisher;
  10. }
  11. public EmployeeDTO createNewEmployee()
  12. {
  13. EmployeeDTO employee = dao.createNewEmployee();
  14. //publishing the veent here
  15. publisher.publishEvent(new EmployeeEvent(this, "ADD", employee));
  16. return employee;
  17. }
  18. }

2.3. 订阅者/监听器 – 实现ApplicationListener接口

为了使 bean 监听某些事件,它必须实现ApplicationListener接口并以onApplicationEvent()方法处理事件。 实际上,Spring 会将所有事件通知给监听器,因此您必须自己过滤事件。

如果使用泛型,Spring 将仅传递与泛型类型参数匹配的消息。 在此示例中,我使用泛型代码仅监听EmployeeEvent

UserEventsProcessor.java

  1. public class UserEventsProcessor implements ApplicationListener<EmployeeEvent>
  2. {
  3. public void onApplicationEvent(EmployeeEvent event)
  4. {
  5. EmployeeEvent employeeEvent = (EmployeeEvent) event;
  6. System.out.println("Employee " + employeeEvent.getEventType()
  7. + " with details : " + employeeEvent.getEmployee());
  8. // Do more processing as per application logic
  9. }
  10. }

2.4. 在应用程序上下文文件中配置 Bean

applicationContext.xml

  1. <context:component-scan base-package="com.howtodoinjava.demo" />
  2. <bean class="com.howtodoinjava.demo.processors.UserEventsProcessor" />

2.5. 示例

TestSpringContext.java

  1. public class TestSpringContext
  2. {
  3. @SuppressWarnings("resource")
  4. public static void main(String[] args) throws Exception
  5. {
  6. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  7. EmployeeController controller = context.getBean(EmployeeController.class);
  8. controller.createNewEmployee();
  9. }
  10. }

观察输出。

Console

  1. INFO: Loading XML bean definitions from class path resource [applicationContext.xml]
  2. Employee ADD with details : Employee [id=1, firstName=Lokesh, lastName=Gupta, type=null]

很棒。 我们现在可以收听事件,然后可以按照自己的方式进行处理。

请注意,应用程序上下文本身还会发布容器事件,例如ContextClosedEventContextRefreshedEventRequestHandledEvent。 如果您的任何 bean 希望收到这些事件的通知,则可以实现ApplicationListener接口。

3. 本示例中使用的其他类文件

这里的EmployeeDTO类如下所示:

EmployeeDTO.java

  1. public class EmployeeDTO
  2. {
  3. private Integer id;
  4. private String firstName;
  5. private String lastName;
  6. private String designation;
  7. public EmployeeDTO(String designation)
  8. {
  9. this.id = -1;
  10. this.firstName = "dummy";
  11. this.lastName = "dummy";
  12. this.designation = designation;
  13. }
  14. public EmployeeDTO() {
  15. // TODO Auto-generated constructor stub
  16. }
  17. //Setters and Getters
  18. @Override
  19. public String toString() {
  20. return "Employee [id=" + id + ", firstName=" + firstName
  21. + ", lastName=" + lastName + ", type=" + designation + "]";
  22. }
  23. }

EmployeeDAO.java

  1. public interface EmployeeDAO
  2. {
  3. public EmployeeDTO createNewEmployee();
  4. }

EmployeeDAOImpl.java

  1. @Repository ("employeeDao")
  2. public class EmployeeDAOImpl implements EmployeeDAO
  3. {
  4. public EmployeeDTO createNewEmployee()
  5. {
  6. EmployeeDTO e = new EmployeeDTO();
  7. e.setId(1);
  8. e.setFirstName("Lokesh");
  9. e.setLastName("Gupta");
  10. return e;
  11. }
  12. public void initBean() {
  13. System.out.println("Init Bean for : EmployeeDAOImpl");
  14. }
  15. public void destroyBean() {
  16. System.out.println("Init Bean for : EmployeeDAOImpl");
  17. }
  18. }

EmployeeManager.java

  1. public interface EmployeeManager
  2. {
  3. public EmployeeDTO createNewEmployee();
  4. }

学习愉快!