Contents
sequence
This article mainly studies spring’s TransactionalEventListener
TransactionalEventListener
org/springframework/transaction/event/TransactionalEventListener.java
/** * An { @link EventListener} that is invoked according to a { @link TransactionPhase}. * * <p>If the event is not published within an active transaction, the event is discarded * unless the { @link #fallbackExecution} flag is explicitly set. If a transaction is * running, the event is processed according to its { @code TransactionPhase}. * * <p>Adding { @link org.springframework.core.annotation.Order @Order } to your annotated * method allows you to prioritize that listener amongst other listeners running before * or after transaction completion. * * @author Stephane Nicoll * @author Sam Brannen * @since 4.2 */ @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @EventListener public @interface TransactionalEventListener { /** * Phase to bind the handling of an event to. * <p>The default phase is { @link TransactionPhase#AFTER_COMMIT}. * <p>If no transaction is in progress, the event is not processed at * all unless { @link #fallbackExecution} has been enabled explicitly. */ TransactionPhase phase () default TransactionPhase.AFTER_COMMIT; /** * Whether the event should be processed if no transaction is running. */ boolean fallbackExecution () default false ; /** * Alias for { @link #classes}. */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] value() default {}; /** * The event classes that this listener handles. * <p>If this attribute is specified with a single value, the annotated * method may optionally accept a single parameter. However, if this * attribute is specified with multiple values, the annotated method * must <em>not</em> declare any parameters. */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] classes() default {}; /** * Spring Expression Language (SpEL) attribute used for making the event *handling conditional. * <p>The default is { @code ""}, meaning the event is always handled. * @see EventListener#condition */ String condition () default "" ; }
TransactionalEventListener is a transaction-aware version of EventListener. The default TransactionPhase is AFTER_COMMIT
ApplicationListenerMethodTransactionalAdapter
org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java
/** * { @link GenericApplicationListener} adapter that delegates the processing of * an event to a { @link TransactionalEventListener} annotated method. Supports * the exact same features as any regular { @link EventListener} annotated method * but is aware of the transactional context of the event publisher. * * <p>Processing of { @link TransactionalEventListener} is enabled automatically * when Spring's transaction management is enabled. For other cases, registering * a bean of type { @link TransactionalEventListenerFactory} is required. * * @author Stephane Nicoll * @author Juergen Hoeller * @since 4.2 * @see ApplicationListenerMethodAdapter * @see TransactionalEventListener */ class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerMethodAdapter { private final TransactionalEventListener annotation ; public ApplicationListenerMethodTransactionalAdapter(String beanName, Class<?> targetClass, Method method) { super (beanName, targetClass, method); TransactionalEventListener ann = AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener. class ); if (ann == null ) { throw new IllegalStateException( "No TransactionalEventListener annotation found on method: " + method); } this . annotation = ann; } @Override public void onApplicationEvent(ApplicationEvent event) { if (TransactionSynchronizationManager.isSynchronizationActive() && TransactionSynchronizationManager.isActualTransactionActive()) { TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event); TransactionSynchronizationManager.registerSynchronization(transactionSynchronization); } else if ( this . annotation .fallbackExecution()) { if ( this . annotation .phase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) { logger.warn( "Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase" ); } processEvent(event); } else { // No transactional event execution at all if (logger.isDebugEnabled()) { logger.debug( "No transaction is active - skipping " + event); } } } private TransactionSynchronization createTransactionSynchronization(ApplicationEvent event) { return new TransactionSynchronizationEventAdapter( this , event, this . annotation .phase()); } private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter { private final ApplicationListenerMethodAdapter listener; private final ApplicationEvent event; private final TransactionPhase phase; public TransactionSynchronizationEventAdapter(ApplicationListenerMethodAdapter listener, ApplicationEvent event, TransactionPhase phase) { this .listener = listener; this .event = event; this .phase = phase; } @Override public int getOrder() { return this .listener.getOrder(); } @Override public void beforeCommit(boolean readOnly) { if ( this .phase == TransactionPhase.BEFORE_COMMIT) { processEvent(); } } @Override public void afterCompletion(int status) { if ( this .phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) { processEvent(); } else if ( this .phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) { processEvent(); } else if ( this .phase == TransactionPhase.AFTER_COMPLETION) { processEvent(); } } protected void processEvent() { this .listener.processEvent( this .event); } } }
ApplicationListenerMethodTransactionalAdapter inherits ApplicationListenerMethodAdapter, and its constructor will find the TransactionalEventListener information of the specified method; its onApplicationEvent method will create and register transactionSynchronization to the current transaction when there is a transaction. If there is no transaction, fallbackExecution will also be executed. processEventTransactionSynchronizationEventAdapter only covers beforeCommit and afterCompletion Two methods, in the afterCompletion method, decide whether to execute processEvent based on the matching relationship between the value of status and the value of phase.
TransactionalEventListenerFactory
org/springframework/transaction/event/TransactionalEventListenerFactory.java
/** * { @link EventListenerFactory} implementation that handles { @link TransactionalEventListener} * annotated methods. * * @author Stephane Nicoll * @since 4.2 */ public class TransactionalEventListenerFactory implements EventListenerFactory , Ordered { private int order = 50 ; public void setOrder ( int order ) { this . order = order; } @Override public int getOrder () { return this . order ; } @Override public boolean supportsMethod ( Method method ) { return AnnotatedElementUtils . hasAnnotation (method, TransactionalEventListener . class ); } @Override public ApplicationListener <?> createApplicationListener ( String beanName, Class<?> type , Method method ) { return new ApplicationListenerMethodTransactionalAdapter (beanName, type , method); } }
TransactionalEventListenerFactory is used to create ApplicationListenerMethodTransactionalAdapter
EventListenerMethodProcessor
org/springframework/context/event/EventListenerMethodProcessor.java
public class EventListenerMethodProcessor implements SmartInitializingSingleton , ApplicationContextAware , BeanFactoryPostProcessor { //...... @Override public void afterSingletonsInstantiated () { ConfigurableListableBeanFactory beanFactory = this . beanFactory ; Assert . state ( this . beanFactory != null , "No ConfigurableListableBeanFactory set" ); String [] beanNames = beanFactory. getBeanNamesForType ( Object . class ); for ( String beanName : beanNames) { if (! ScopedProxyUtils . isScopedTarget (beanName)) { Class <?> type = null ; try { type = AutoProxyUtils . determineTargetClass (beanFactory, beanName); } catch ( Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger. isDebugEnabled ()) { logger.debug ( "Could not resolve target class for bean with name '" + beanName + "'" , ex); } } if ( type != null ) { if ( ScopedObject . class . isAssignableFrom ( type )) { try { Class <?> targetClass = AutoProxyUtils . determineTargetClass ( beanFactory, ScopedProxyUtils . getTargetBeanName (beanName)); if (targetClass != null ) { type = targetClass; } } catch ( Throwable ex) { // An invalid scoped proxy arrangement - let's ignore it. if (logger. isDebugEnabled ()) { logger.debug ( " Could not resolve target bean for scoped proxy '" + beanName + "'" , ex); } } } try { processBean (beanName, type ); } catch ( Throwable ex) { throw new BeanInitializationException ( "Failed to process @EventListener " + "annotation on bean with name '" + beanName + "'" , ex); } } } } } private void processBean ( final String beanName, final Class<?> targetType ) { if (! this . nonAnnotatedClasses . contains (targetType) && AnnotationUtils . isCandidateClass (targetType, EventListener . class ) && ! isSpringContainerClass (targetType)) { Map < Method , EventListener > annotatedMethods = null ; try { annotatedMethods = MethodIntrospector . selectMethods (targetType, ( MethodIntrospector . MetadataLookup < EventListener >) method -> AnnotatedElementUtils . findMergedAnnotation (method, EventListener . class )); } catch ( Throwable ex) { // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. if (logger. isDebugEnabled ()) { logger.debug ( " Could not resolve methods for bean with name '" + beanName + "'" , ex); } } if ( CollectionUtils . isEmpty (annotatedMethods)) { this . nonAnnotatedClasses . add (targetType); if (logger. isTraceEnabled ()) { logger.trace ( "No @EventListener annotations found on bean class: " + targetType.getName ( )); } } else { // Non-empty set of methods ConfigurableApplicationContext context = this . applicationContext ; Assert . state (context != null , "No ApplicationContext set" ); List < EventListenerFactory > factories = this . eventListenerFactories ; Assert . state (factories != null , "EventListenerFactory List not initialized" ); for ( Method method : annotatedMethods. keySet ()) { for ( EventListenerFactory factory : factories) { if (factory. supportsMethod (method)) { Method methodToUse = AopUtils . selectInvocableMethod (method, context . getType (beanName)); ApplicationListener <?> applicationListener = factory. createApplicationListener (beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter ) { (( ApplicationListenerMethodAdapter ) applicationListener). init (context, this . evaluator ); } context. addApplicationListener (applicationListener); break ; } } } if (logger. isDebugEnabled ()) { logger. debug (annotatedMethods. size () + " @EventListener methods processed on bean '" + beanName + "': " + annotatedMethods); } } } } //... }
EventListenerMethodProcessor implements the SmartInitializingSingleton interface. Its afterSingletonsInstantiated method first determines the type and then executes processBean. This method will first collect annotatedMethods, then traverse the method, and execute createApplicationListener for the factory that supports this method during the traversal of factories, and add it to the context.
summary
TransactionalEventListener is the transaction-aware version of EventListener. The default TransactionPhase is AFTER_COMMIT. TransactionSynchronizationEventAdapter only covers the beforeCommit and afterCompletion methods. In the afterCompletion method, it is decided whether to execute the processEvent based on the matching relationship between the status value and the phase value, so it is thrown here Exceptions will be caught and logged