package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-event-");
executor.initialize();
return executor;
}
}
package com.example.demo.event;
import com.example.demo.model.User;
import java.time.LocalDateTime;
public class UserRegisteredEvent {
private final User user;
private final LocalDateTime timestamp;
public UserRegisteredEvent(User user) {
this.user = user;
this.timestamp = LocalDateTime.now();
}
public User getUser() {
return user;
}
public LocalDateTime getTimestamp() {
return timestamp;
}
}
package com.example.demo.service;
import com.example.demo.event.UserRegisteredEvent;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
private final UserRepository userRepository;
private final ApplicationEventPublisher eventPublisher;
public UserService(UserRepository userRepository,
ApplicationEventPublisher eventPublisher) {
this.userRepository = userRepository;
this.eventPublisher = eventPublisher;
}
@Transactional
public User registerUser(User user) {
User savedUser = userRepository.save(user);
// Publish event - listeners will handle side effects
eventPublisher.publishEvent(new UserRegisteredEvent(savedUser));
return savedUser;
}
}
package com.example.demo.listener;
import com.example.demo.event.UserRegisteredEvent;
import com.example.demo.service.EmailService;
import com.example.demo.service.AnalyticsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.event.TransactionPhase;
@Component
public class UserEventListener {
private static final Logger logger = LoggerFactory.getLogger(UserEventListener.class);
private final EmailService emailService;
private final AnalyticsService analyticsService;
public UserEventListener(EmailService emailService,
AnalyticsService analyticsService) {
this.emailService = emailService;
this.analyticsService = analyticsService;
}
// Executes after transaction commits
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(1)
public void sendWelcomeEmail(UserRegisteredEvent event) {
logger.info("Sending welcome email to: {}", event.getUser().getEmail());
emailService.sendWelcomeEmail(event.getUser());
}
// Async event listener
@EventListener
@Async
@Order(2)
public void trackRegistration(UserRegisteredEvent event) {
logger.info("Tracking user registration analytics");
analyticsService.trackUserRegistration(event.getUser().getId());
}
// Synchronous listener
@EventListener
@Order(3)
public void logRegistration(UserRegisteredEvent event) {
logger.info("User registered: {} at {}",
event.getUser().getEmail(),
event.getTimestamp());
}
// Conditional listener
@EventListener(condition = "#event.user.email.endsWith('@company.com')")
public void handleInternalUser(UserRegisteredEvent event) {
logger.info("Internal user registered: {}", event.getUser().getEmail());
// Special handling for internal users
}
}
Spring's event mechanism enables loose coupling between components. ApplicationEventPublisher publishes events. @EventListener handles events asynchronously or synchronously. Events extend ApplicationEvent or are POJOs. @TransactionalEventListener publishes after transaction commit, ensuring consistency. Event ordering uses @Order annotation. Async events with @Async improve responsiveness. Events decouple business logic—order creation triggers email, inventory update, analytics. Domain events model business occurrences. Event sourcing stores events as primary data source. Spring Cloud Stream extends events across microservices. Proper event design prevents tight coupling while enabling reactive, scalable architectures. Events are fundamental to modern application design.