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.javasrc/com/yaruyng/context/ ├── ApplicationEvent.java ├── ApplicationListener.java ├── ApplicationEventPublisher.java ├── SimpleApplicationEventPublisher.java ├── ApplicationContextEvent.java ├── ContextRefreshEvent.java └── ContextRefreshedEvent.javasrc/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:
- Extends EventObject from Java standard library
- Serializable support
- Source object tracking
- 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:
- Generic type support
- Single responsibility principle
- 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:
- Listener management
- Event broadcasting
- 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
- Event Design
- Clear event hierarchy
- Immutable event data
- Meaningful event names
- Listener Implementation
- Single responsibility
- Error handling
- Performance consideration
- Event Publishing
- Appropriate timing
- Error propagation
- Transaction boundaries
Common Challenges and Solutions
- Event Ordering
- Listener priority
- Synchronous processing
- Event queuing
- Error Handling
- Exception propagation
- Listener isolation
- Recovery mechanisms
- 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
暂无评论内容