Let’s talk about spring’s TransactionalEventListener

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

doc