Spring Architecture Series-8.Implementing Event Publishing and Listening Mechanism

Introduction

Event-driven programming is a powerful paradigm that enables loose coupling between components in an application. In this article, I’ll explore how to implement an event publishing and listening mechanism in a Spring-like framework, based on my miniSpring project’s implementation.

Core Components

The event mechanism implementation consists of several key components:

src/com/yaruyng/context/
├── ApplicationEvent.java
├── ApplicationListener.java
├── ApplicationEventPublisher.java
├── SimpleApplicationEventPublisher.java
├── ApplicationContextEvent.java
├── ContextRefreshEvent.java
└── ContextRefreshedEvent.java
src/com/yaruyng/context/
├── ApplicationEvent.java
├── ApplicationListener.java
├── ApplicationEventPublisher.java
├── SimpleApplicationEventPublisher.java
├── ApplicationContextEvent.java
├── ContextRefreshEvent.java
└── ContextRefreshedEvent.java
src/com/yaruyng/context/ ├── ApplicationEvent.java ├── ApplicationListener.java ├── ApplicationEventPublisher.java ├── SimpleApplicationEventPublisher.java ├── ApplicationContextEvent.java ├── ContextRefreshEvent.java └── ContextRefreshedEvent.java

Enter fullscreen mode Exit fullscreen mode

Event Base Class

The ApplicationEvent class serves as the base for all application events:

<span>public</span> <span>class</span> <span>ApplicationEvent</span> <span>extends</span> <span>EventObject</span> <span>{</span>
<span>private</span> <span>static</span> <span>final</span> <span>long</span> <span>serialVersionUID</span> <span>=</span> <span>1L</span><span>;</span>
<span>protected</span> <span>String</span> <span>msg</span> <span>=</span> <span>null</span><span>;</span>
<span>public</span> <span>ApplicationEvent</span><span>(</span><span>Object</span> <span>source</span><span>)</span> <span>{</span>
<span>super</span><span>(</span><span>source</span><span>);</span>
<span>this</span><span>.</span><span>msg</span> <span>=</span> <span>source</span><span>.</span><span>toString</span><span>();</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>ApplicationEvent</span> <span>extends</span> <span>EventObject</span> <span>{</span>
    <span>private</span> <span>static</span> <span>final</span> <span>long</span> <span>serialVersionUID</span> <span>=</span> <span>1L</span><span>;</span>
    <span>protected</span> <span>String</span> <span>msg</span> <span>=</span> <span>null</span><span>;</span>

    <span>public</span> <span>ApplicationEvent</span><span>(</span><span>Object</span> <span>source</span><span>)</span> <span>{</span>
        <span>super</span><span>(</span><span>source</span><span>);</span>
        <span>this</span><span>.</span><span>msg</span> <span>=</span> <span>source</span><span>.</span><span>toString</span><span>();</span>
    <span>}</span>
<span>}</span>
public class ApplicationEvent extends EventObject { private static final long serialVersionUID = 1L; protected String msg = null; public ApplicationEvent(Object source) { super(source); this.msg = source.toString(); } }

Enter fullscreen mode Exit fullscreen mode

Key features:

  1. Extends EventObject from Java standard library
  2. Serializable support
  3. Source object tracking
  4. Message support

Event Listener Interface

The ApplicationListener interface defines the contract for event listeners:

<span>public</span> <span>interface</span> <span>ApplicationListener</span><span><</span><span>E</span> <span>extends</span> <span>ApplicationEvent</span><span>></span> <span>extends</span> <span>EventListener</span> <span>{</span>
<span>void</span> <span>onApplicationEvent</span><span>(</span><span>E</span> <span>event</span><span>);</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>ApplicationListener</span><span><</span><span>E</span> <span>extends</span> <span>ApplicationEvent</span><span>></span> <span>extends</span> <span>EventListener</span> <span>{</span>
    <span>void</span> <span>onApplicationEvent</span><span>(</span><span>E</span> <span>event</span><span>);</span>
<span>}</span>
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E event); }

Enter fullscreen mode Exit fullscreen mode

Features:

  1. Generic type support
  2. Single responsibility principle
  3. Clear event handling contract

Event Publisher

The SimpleApplicationEventPublisher implements the event publishing mechanism:

<span>public</span> <span>class</span> <span>SimpleApplicationEventPublisher</span> <span>implements</span> <span>ApplicationEventPublisher</span> <span>{</span>
<span>List</span><span><</span><span>ApplicationListener</span><span>></span> <span>listeners</span> <span>=</span> <span>new</span> <span>ArrayList</span><span><>();</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>publishEvent</span><span>(</span><span>ApplicationEvent</span> <span>event</span><span>)</span> <span>{</span>
<span>for</span> <span>(</span><span>ApplicationListener</span> <span>listener</span> <span>:</span> <span>listeners</span><span>)</span> <span>{</span>
<span>listener</span><span>.</span><span>onApplicationEvent</span><span>(</span><span>event</span><span>);</span>
<span>}</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>addApplicationListener</span><span>(</span><span>ApplicationListener</span> <span>listener</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>listeners</span><span>.</span><span>add</span><span>(</span><span>listener</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>SimpleApplicationEventPublisher</span> <span>implements</span> <span>ApplicationEventPublisher</span> <span>{</span>
    <span>List</span><span><</span><span>ApplicationListener</span><span>></span> <span>listeners</span> <span>=</span> <span>new</span> <span>ArrayList</span><span><>();</span>

    <span>@Override</span>
    <span>public</span> <span>void</span> <span>publishEvent</span><span>(</span><span>ApplicationEvent</span> <span>event</span><span>)</span> <span>{</span>
        <span>for</span> <span>(</span><span>ApplicationListener</span> <span>listener</span> <span>:</span> <span>listeners</span><span>)</span> <span>{</span>
            <span>listener</span><span>.</span><span>onApplicationEvent</span><span>(</span><span>event</span><span>);</span>
        <span>}</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>void</span> <span>addApplicationListener</span><span>(</span><span>ApplicationListener</span> <span>listener</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>listeners</span><span>.</span><span>add</span><span>(</span><span>listener</span><span>);</span>
    <span>}</span>
<span>}</span>
public class SimpleApplicationEventPublisher implements ApplicationEventPublisher { List<ApplicationListener> listeners = new ArrayList<>(); @Override public void publishEvent(ApplicationEvent event) { for (ApplicationListener listener : listeners) { listener.onApplicationEvent(event); } } @Override public void addApplicationListener(ApplicationListener listener) { this.listeners.add(listener); } }

Enter fullscreen mode Exit fullscreen mode

Key aspects:

  1. Listener management
  2. Event broadcasting
  3. Synchronous event processing

Context Events

1. Context Refresh Event

<span>public</span> <span>class</span> <span>ContextRefreshEvent</span> <span>extends</span> <span>ApplicationContextEvent</span> <span>{</span>
<span>public</span> <span>ContextRefreshEvent</span><span>(</span><span>ApplicationContext</span> <span>source</span><span>)</span> <span>{</span>
<span>super</span><span>(</span><span>source</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>ContextRefreshEvent</span> <span>extends</span> <span>ApplicationContextEvent</span> <span>{</span>
    <span>public</span> <span>ContextRefreshEvent</span><span>(</span><span>ApplicationContext</span> <span>source</span><span>)</span> <span>{</span>
        <span>super</span><span>(</span><span>source</span><span>);</span>
    <span>}</span>
<span>}</span>
public class ContextRefreshEvent extends ApplicationContextEvent { public ContextRefreshEvent(ApplicationContext source) { super(source); } }

Enter fullscreen mode Exit fullscreen mode

2. Context Refreshed Event

<span>public</span> <span>class</span> <span>ContextRefreshedEvent</span> <span>extends</span> <span>ApplicationContextEvent</span> <span>{</span>
<span>public</span> <span>ContextRefreshedEvent</span><span>(</span><span>ApplicationContext</span> <span>source</span><span>)</span> <span>{</span>
<span>super</span><span>(</span><span>source</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>ContextRefreshedEvent</span> <span>extends</span> <span>ApplicationContextEvent</span> <span>{</span>
    <span>public</span> <span>ContextRefreshedEvent</span><span>(</span><span>ApplicationContext</span> <span>source</span><span>)</span> <span>{</span>
        <span>super</span><span>(</span><span>source</span><span>);</span>
    <span>}</span>
<span>}</span>
public class ContextRefreshedEvent extends ApplicationContextEvent { public ContextRefreshedEvent(ApplicationContext source) { super(source); } }

Enter fullscreen mode Exit fullscreen mode

Integration with Application Context

The AbstractApplicationContext integrates event support:

<span>public</span> <span>abstract</span> <span>class</span> <span>AbstractApplicationContext</span> <span>implements</span> <span>ApplicationContext</span> <span>{</span>
<span>private</span> <span>ApplicationEventPublisher</span> <span>applicationEventPublisher</span><span>;</span>
<span>public</span> <span>abstract</span> <span>void</span> <span>registerListeners</span><span>();</span>
<span>public</span> <span>abstract</span> <span>void</span> <span>initApplicationEventPublisher</span><span>();</span>
<span>public</span> <span>void</span> <span>refresh</span><span>()</span> <span>throws</span> <span>BeansException</span><span>,</span> <span>IllegalStateException</span> <span>{</span>
<span>// Initialize event publisher</span>
<span>initApplicationEventPublisher</span><span>();</span>
<span>// Register listeners</span>
<span>registerListeners</span><span>();</span>
<span>// Other initialization steps...</span>
<span>// Publish refresh event</span>
<span>finishRefresh</span><span>();</span>
<span>}</span>
<span>public</span> <span>void</span> <span>finishRefresh</span><span>()</span> <span>{</span>
<span>publishEvent</span><span>(</span><span>new</span> <span>ContextRefreshedEvent</span><span>(</span><span>this</span><span>));</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>abstract</span> <span>class</span> <span>AbstractApplicationContext</span> <span>implements</span> <span>ApplicationContext</span> <span>{</span>
    <span>private</span> <span>ApplicationEventPublisher</span> <span>applicationEventPublisher</span><span>;</span>

    <span>public</span> <span>abstract</span> <span>void</span> <span>registerListeners</span><span>();</span>
    <span>public</span> <span>abstract</span> <span>void</span> <span>initApplicationEventPublisher</span><span>();</span>

    <span>public</span> <span>void</span> <span>refresh</span><span>()</span> <span>throws</span> <span>BeansException</span><span>,</span> <span>IllegalStateException</span> <span>{</span>
        <span>// Initialize event publisher</span>
        <span>initApplicationEventPublisher</span><span>();</span>

        <span>// Register listeners</span>
        <span>registerListeners</span><span>();</span>

        <span>// Other initialization steps...</span>

        <span>// Publish refresh event</span>
        <span>finishRefresh</span><span>();</span>
    <span>}</span>

    <span>public</span> <span>void</span> <span>finishRefresh</span><span>()</span> <span>{</span>
        <span>publishEvent</span><span>(</span><span>new</span> <span>ContextRefreshedEvent</span><span>(</span><span>this</span><span>));</span>
    <span>}</span>
<span>}</span>
public abstract class AbstractApplicationContext implements ApplicationContext { private ApplicationEventPublisher applicationEventPublisher; public abstract void registerListeners(); public abstract void initApplicationEventPublisher(); public void refresh() throws BeansException, IllegalStateException { // Initialize event publisher initApplicationEventPublisher(); // Register listeners registerListeners(); // Other initialization steps... // Publish refresh event finishRefresh(); } public void finishRefresh() { publishEvent(new ContextRefreshedEvent(this)); } }

Enter fullscreen mode Exit fullscreen mode

Event Processing Flow

1. Event Register

<span>public</span> <span>void</span> <span>registerListeners</span><span>()</span> <span>{</span>
<span>String</span><span>[]</span> <span>beanDefinitionNames</span> <span>=</span> <span>this</span><span>.</span><span>getBeanFactory</span><span>().</span><span>getBeanDefinitionNames</span><span>();</span>
<span>for</span> <span>(</span><span>String</span> <span>bdName</span> <span>:</span> <span>beanDefinitionNames</span><span>)</span> <span>{</span>
<span>Object</span> <span>bean</span> <span>=</span> <span>getBean</span><span>(</span><span>bdName</span><span>);</span>
<span>if</span><span>(</span><span>bean</span> <span>instanceof</span> <span>ApplicationListener</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>getApplicationEventPublisher</span><span>()</span>
<span>.</span><span>addApplicationListener</span><span>((</span><span>ApplicationListener</span><span><?>)</span> <span>bean</span><span>);</span>
<span>}</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>void</span> <span>registerListeners</span><span>()</span> <span>{</span>
    <span>String</span><span>[]</span> <span>beanDefinitionNames</span> <span>=</span> <span>this</span><span>.</span><span>getBeanFactory</span><span>().</span><span>getBeanDefinitionNames</span><span>();</span>
    <span>for</span> <span>(</span><span>String</span> <span>bdName</span> <span>:</span> <span>beanDefinitionNames</span><span>)</span> <span>{</span>
        <span>Object</span> <span>bean</span> <span>=</span> <span>getBean</span><span>(</span><span>bdName</span><span>);</span>
        <span>if</span><span>(</span><span>bean</span> <span>instanceof</span> <span>ApplicationListener</span><span>)</span> <span>{</span>
            <span>this</span><span>.</span><span>getApplicationEventPublisher</span><span>()</span>
                <span>.</span><span>addApplicationListener</span><span>((</span><span>ApplicationListener</span><span><?>)</span> <span>bean</span><span>);</span>
        <span>}</span>
    <span>}</span>
<span>}</span>
public void registerListeners() { String[] beanDefinitionNames = this.getBeanFactory().getBeanDefinitionNames(); for (String bdName : beanDefinitionNames) { Object bean = getBean(bdName); if(bean instanceof ApplicationListener) { this.getApplicationEventPublisher() .addApplicationListener((ApplicationListener<?>) bean); } } }

Enter fullscreen mode Exit fullscreen mode

2. Event Publishing

<span>public</span> <span>void</span> <span>publishEvent</span><span>(</span><span>ApplicationEvent</span> <span>event</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>getApplicationEventPublisher</span><span>().</span><span>publishEvent</span><span>(</span><span>event</span><span>);</span>
<span>}</span>
<span>public</span> <span>void</span> <span>publishEvent</span><span>(</span><span>ApplicationEvent</span> <span>event</span><span>)</span> <span>{</span>
    <span>this</span><span>.</span><span>getApplicationEventPublisher</span><span>().</span><span>publishEvent</span><span>(</span><span>event</span><span>);</span>
<span>}</span>
public void publishEvent(ApplicationEvent event) { this.getApplicationEventPublisher().publishEvent(event); }

Enter fullscreen mode Exit fullscreen mode

Usage Example

1. Creating Custom Events

<span>public</span> <span>class</span> <span>UserRegisteredEvent</span> <span>extends</span> <span>ApplicationEvent</span> <span>{</span>
<span>private</span> <span>final</span> <span>User</span> <span>user</span><span>;</span>
<span>public</span> <span>UserRegisteredEvent</span><span>(</span><span>Object</span> <span>source</span><span>,</span> <span>User</span> <span>user</span><span>)</span> <span>{</span>
<span>super</span><span>(</span><span>source</span><span>);</span>
<span>this</span><span>.</span><span>user</span> <span>=</span> <span>user</span><span>;</span>
<span>}</span>
<span>public</span> <span>User</span> <span>getUser</span><span>()</span> <span>{</span>
<span>return</span> <span>user</span><span>;</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>UserRegisteredEvent</span> <span>extends</span> <span>ApplicationEvent</span> <span>{</span>
    <span>private</span> <span>final</span> <span>User</span> <span>user</span><span>;</span>

    <span>public</span> <span>UserRegisteredEvent</span><span>(</span><span>Object</span> <span>source</span><span>,</span> <span>User</span> <span>user</span><span>)</span> <span>{</span>
        <span>super</span><span>(</span><span>source</span><span>);</span>
        <span>this</span><span>.</span><span>user</span> <span>=</span> <span>user</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>User</span> <span>getUser</span><span>()</span> <span>{</span>
        <span>return</span> <span>user</span><span>;</span>
    <span>}</span>
<span>}</span>
public class UserRegisteredEvent extends ApplicationEvent { private final User user; public UserRegisteredEvent(Object source, User user) { super(source); this.user = user; } public User getUser() { return user; } }

Enter fullscreen mode Exit fullscreen mode

2. Implementing Event Listeners

<span>@Component</span>
<span>public</span> <span>class</span> <span>EmailNotificationListener</span> <span>implements</span> <span>ApplicationListener</span><span><</span><span>UserRegisteredEvent</span><span>></span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>onApplicationEvent</span><span>(</span><span>UserRegisteredEvent</span> <span>event</span><span>)</span> <span>{</span>
<span>User</span> <span>user</span> <span>=</span> <span>event</span><span>.</span><span>getUser</span><span>();</span>
<span>// Send welcome email</span>
<span>sendWelcomeEmail</span><span>(</span><span>user</span><span>);</span>
<span>}</span>
<span>}</span>
<span>@Component</span>
<span>public</span> <span>class</span> <span>EmailNotificationListener</span> <span>implements</span> <span>ApplicationListener</span><span><</span><span>UserRegisteredEvent</span><span>></span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>onApplicationEvent</span><span>(</span><span>UserRegisteredEvent</span> <span>event</span><span>)</span> <span>{</span>
        <span>User</span> <span>user</span> <span>=</span> <span>event</span><span>.</span><span>getUser</span><span>();</span>
        <span>// Send welcome email</span>
        <span>sendWelcomeEmail</span><span>(</span><span>user</span><span>);</span>
    <span>}</span>
<span>}</span>
@Component public class EmailNotificationListener implements ApplicationListener<UserRegisteredEvent> { @Override public void onApplicationEvent(UserRegisteredEvent event) { User user = event.getUser(); // Send welcome email sendWelcomeEmail(user); } }

Enter fullscreen mode Exit fullscreen mode

3. Publishing Event

<span>@Service</span>
<span>public</span> <span>class</span> <span>UserService</span> <span>{</span>
<span>@Autowired</span>
<span>private</span> <span>ApplicationEventPublisher</span> <span>eventPublisher</span><span>;</span>
<span>public</span> <span>void</span> <span>registerUser</span><span>(</span><span>User</span> <span>user</span><span>)</span> <span>{</span>
<span>// Save user</span>
<span>userRepository</span><span>.</span><span>save</span><span>(</span><span>user</span><span>);</span>
<span>// Publish event</span>
<span>eventPublisher</span><span>.</span><span>publishEvent</span><span>(</span><span>new</span> <span>UserRegisteredEvent</span><span>(</span><span>this</span><span>,</span> <span>user</span><span>));</span>
<span>}</span>
<span>}</span>
<span>@Service</span>
<span>public</span> <span>class</span> <span>UserService</span> <span>{</span>
    <span>@Autowired</span>
    <span>private</span> <span>ApplicationEventPublisher</span> <span>eventPublisher</span><span>;</span>

    <span>public</span> <span>void</span> <span>registerUser</span><span>(</span><span>User</span> <span>user</span><span>)</span> <span>{</span>
        <span>// Save user</span>
        <span>userRepository</span><span>.</span><span>save</span><span>(</span><span>user</span><span>);</span>

        <span>// Publish event</span>
        <span>eventPublisher</span><span>.</span><span>publishEvent</span><span>(</span><span>new</span> <span>UserRegisteredEvent</span><span>(</span><span>this</span><span>,</span> <span>user</span><span>));</span>
    <span>}</span>
<span>}</span>
@Service public class UserService { @Autowired private ApplicationEventPublisher eventPublisher; public void registerUser(User user) { // Save user userRepository.save(user); // Publish event eventPublisher.publishEvent(new UserRegisteredEvent(this, user)); } }

Enter fullscreen mode Exit fullscreen mode

Key Features

1.Event Types

  • Context events
  • Custom events
  • Event hierarchy ### 2. Listener Management
  • Dynamic registration
  • Type-safe handling
  • Multiple listeners ### 3. Event Publishing
  • Synchronous processing
  • Error handling
  • Event ordering

Best Practice

  1. Event Design
    • Clear event hierarchy
    • Immutable event data
    • Meaningful event names
  2. Listener Implementation
    • Single responsibility
    • Error handling
    • Performance consideration
  3. Event Publishing
    • Appropriate timing
    • Error propagation
    • Transaction boundaries

Common Challenges and Solutions

  1. Event Ordering
    • Listener priority
    • Synchronous processing
    • Event queuing
  2. Error Handling
    • Exception propagation
    • Listener isolation
    • Recovery mechanisms
  3. Performance
    • Asynchronous processing
    • Event filtering
    • Listener optimization

Conclusion

Implementing an event mechanism provides:

  • Loose coupling between components
  • Asynchronous communication
  • Extensible architecture
  • Decoupled business logic Key takeaways:
  • Understanding event-driven architecture
  • Event listener patterns
  • Event publishing mechanisms
  • Integration with IoC container

This implementation demonstrates how to create a robust event system while maintaining simplicity and flexibility.

原文链接:Spring Architecture Series-8.Implementing Event Publishing and Listening Mechanism

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
Change your thoughts and you change your world.
改变你的思想,你就能改变自己的命运
评论 抢沙发

请登录后发表评论

    暂无评论内容