{"id":10008267,"date":"2023-09-11T16:15:46","date_gmt":"2023-09-11T08:15:46","guid":{"rendered":"https:\/\/blog.itmax.cn\/?p=10008267"},"modified":"2023-09-11T16:15:46","modified_gmt":"2023-09-11T08:15:46","slug":"%e8%81%8a%e8%81%8aspring%e7%9a%84transactionaleventlistener","status":"publish","type":"post","link":"https:\/\/itmax.cn\/?p=10008267","title":{"rendered":"\u804a\u804aspring\u7684TransactionalEventListener"},"content":{"rendered":"<h2>\u5e8f<\/h2>\n<p>\u672c\u6587\u4e3b\u8981\u7814\u7a76\u4e00\u4e0bspring\u7684TransactionalEventListener<\/p>\n<h2>TransactionalEventListener<\/h2>\n<p>org\/springframework\/transaction\/event\/TransactionalEventListener.java<\/p>\n<pre><code>\/**\n * An {@link EventListener} that is invoked according to a {@link TransactionPhase}.\n *\n * &lt;p&gt;If the event is not published within an active transaction, the event is discarded\n * unless the {@link #fallbackExecution} flag is explicitly set. If a transaction is\n * running, the event is processed according to its {@code TransactionPhase}.\n *\n * &lt;p&gt;Adding {@link org.springframework.core.annotation.Order @Order} to your annotated\n * method allows you to prioritize that listener amongst other listeners running before\n * or after transaction completion.\n *\n * @author Stephane Nicoll\n * @author Sam Brannen\n * @since 4.2\n *\/\n@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@EventListener\npublic @interface TransactionalEventListener {\n\n    \/**\n     * Phase to bind the handling of an event to.\n     * &lt;p&gt;The default phase is {@link TransactionPhase#AFTER_COMMIT}.\n     * &lt;p&gt;If no transaction is in progress, the event is not processed at\n     * all unless {@link #fallbackExecution} has been enabled explicitly.\n     *\/\n    TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;\n\n    \/**\n     * Whether the event should be processed if no transaction is running.\n     *\/\n    boolean fallbackExecution() default false;\n\n    \/**\n     * Alias for {@link #classes}.\n     *\/\n    @AliasFor(annotation = EventListener.class, attribute = &quot;classes&quot;)\n    Class&lt;?&gt;[] value() default {};\n\n    \/**\n     * The event classes that this listener handles.\n     * &lt;p&gt;If this attribute is specified with a single value, the annotated\n     * method may optionally accept a single parameter. However, if this\n     * attribute is specified with multiple values, the annotated method\n     * must &lt;em&gt;not&lt;\/em&gt; declare any parameters.\n     *\/\n    @AliasFor(annotation = EventListener.class, attribute = &quot;classes&quot;)\n    Class&lt;?&gt;[] classes() default {};\n\n    \/**\n     * Spring Expression Language (SpEL) attribute used for making the event\n     * handling conditional.\n     * &lt;p&gt;The default is {@code &quot;&quot;}, meaning the event is always handled.\n     * @see EventListener#condition\n     *\/\n    String condition() default &quot;&quot;;\n\n}<\/code><\/pre>\n<blockquote><p>TransactionalEventListener\u662fEventListener\u7684\u4e8b\u52a1\u611f\u77e5\u7248\u672c\uff0c\u9ed8\u8ba4\u7684\u662fTransactionPhase\u662fAFTER_COMMIT<\/p><\/blockquote>\n<h2>ApplicationListenerMethodTransactionalAdapter<\/h2>\n<p>org\/springframework\/transaction\/event\/ApplicationListenerMethodTransactionalAdapter.java<\/p>\n<pre><code>\/**\n * {@link GenericApplicationListener} adapter that delegates the processing of\n * an event to a {@link TransactionalEventListener} annotated method. Supports\n * the exact same features as any regular {@link EventListener} annotated method\n * but is aware of the transactional context of the event publisher.\n *\n * &lt;p&gt;Processing of {@link TransactionalEventListener} is enabled automatically\n * when Spring's transaction management is enabled. For other cases, registering\n * a bean of type {@link TransactionalEventListenerFactory} is required.\n *\n * @author Stephane Nicoll\n * @author Juergen Hoeller\n * @since 4.2\n * @see ApplicationListenerMethodAdapter\n * @see TransactionalEventListener\n *\/\nclass ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerMethodAdapter {\n\n    private final TransactionalEventListener annotation;\n\n\n    public ApplicationListenerMethodTransactionalAdapter(String beanName, Class&lt;?&gt; targetClass, Method method) {\n        super(beanName, targetClass, method);\n        TransactionalEventListener ann = AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class);\n        if (ann == null) {\n            throw new IllegalStateException(&quot;No TransactionalEventListener annotation found on method: &quot; + method);\n        }\n        this.annotation = ann;\n    }\n\n\n    @Override\n    public void onApplicationEvent(ApplicationEvent event) {\n        if (TransactionSynchronizationManager.isSynchronizationActive()\n                &amp;&amp; TransactionSynchronizationManager.isActualTransactionActive()) {\n            TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event);\n            TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);\n        }\n        else if (this.annotation.fallbackExecution()) {\n            if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK &amp;&amp; logger.isWarnEnabled()) {\n                logger.warn(&quot;Processing &quot; + event + &quot; as a fallback execution on AFTER_ROLLBACK phase&quot;);\n            }\n            processEvent(event);\n        }\n        else {\n            \/\/ No transactional event execution at all\n            if (logger.isDebugEnabled()) {\n                logger.debug(&quot;No transaction is active - skipping &quot; + event);\n            }\n        }\n    }\n\n    private TransactionSynchronization createTransactionSynchronization(ApplicationEvent event) {\n        return new TransactionSynchronizationEventAdapter(this, event, this.annotation.phase());\n    }\n\n\n    private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter {\n\n        private final ApplicationListenerMethodAdapter listener;\n\n        private final ApplicationEvent event;\n\n        private final TransactionPhase phase;\n\n        public TransactionSynchronizationEventAdapter(ApplicationListenerMethodAdapter listener,\n                ApplicationEvent event, TransactionPhase phase) {\n\n            this.listener = listener;\n            this.event = event;\n            this.phase = phase;\n        }\n\n        @Override\n        public int getOrder() {\n            return this.listener.getOrder();\n        }\n\n        @Override\n        public void beforeCommit(boolean readOnly) {\n            if (this.phase == TransactionPhase.BEFORE_COMMIT) {\n                processEvent();\n            }\n        }\n\n        @Override\n        public void afterCompletion(int status) {\n            if (this.phase == TransactionPhase.AFTER_COMMIT &amp;&amp; status == STATUS_COMMITTED) {\n                processEvent();\n            }\n            else if (this.phase == TransactionPhase.AFTER_ROLLBACK &amp;&amp; status == STATUS_ROLLED_BACK) {\n                processEvent();\n            }\n            else if (this.phase == TransactionPhase.AFTER_COMPLETION) {\n                processEvent();\n            }\n        }\n\n        protected void processEvent() {\n            this.listener.processEvent(this.event);\n        }\n    }\n\n}<\/code><\/pre>\n<blockquote><p>ApplicationListenerMethodTransactionalAdapter\u7ee7\u627f\u4e86ApplicationListenerMethodAdapter\uff0c\u5b83\u7684\u6784\u9020\u5668\u4f1a\u627e\u5230\u6307\u5b9a\u65b9\u6cd5\u7684TransactionalEventListener\u4fe1\u606f\uff1b\u5176onApplicationEvent\u65b9\u6cd5\u5728\u6709\u4e8b\u52a1\u7684\u65f6\u5019\u4f1a\u521b\u5efa\u5e76\u6ce8\u518ctransactionSynchronization\u5230\u5f53\u524d\u4e8b\u52a1\uff0c\u6ca1\u6709\u4e8b\u52a1\u82e5\u5141\u8bb8fallbackExecution\u4e5f\u4f1a\u6267\u884cprocessEvent<br \/>TransactionSynchronizationEventAdapter\u53ea\u662f\u8986\u76d6\u4e86beforeCommit\u53caafterCompletion\u4e24\u4e2a\u65b9\u6cd5\uff0c\u5728afterCompletion\u65b9\u6cd5\u4e2d\u6839\u636estatus\u7684\u503c\u4e0ephase\u7684\u503c\u7684\u5339\u914d\u5173\u7cfb\u51b3\u5b9a\u662f\u5426\u6267\u884cprocessEvent<\/p><\/blockquote>\n<h2>TransactionalEventListenerFactory<\/h2>\n<p>org\/springframework\/transaction\/event\/TransactionalEventListenerFactory.java<\/p>\n<pre><code>\/**\n * {@link EventListenerFactory} implementation that handles {@link TransactionalEventListener}\n * annotated methods.\n *\n * @author Stephane Nicoll\n * @since 4.2\n *\/\npublic class TransactionalEventListenerFactory implements EventListenerFactory, Ordered {\n\n    private int order = 50;\n\n\n    public void setOrder(int order) {\n        this.order = order;\n    }\n\n    @Override\n    public int getOrder() {\n        return this.order;\n    }\n\n\n    @Override\n    public boolean supportsMethod(Method method) {\n        return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class);\n    }\n\n    @Override\n    public ApplicationListener&lt;?&gt; createApplicationListener(String beanName, Class&lt;?&gt; type, Method method) {\n        return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);\n    }\n\n}<\/code><\/pre>\n<blockquote><p>TransactionalEventListenerFactory\u7528\u4e8e\u521b\u5efaApplicationListenerMethodTransactionalAdapter<\/p><\/blockquote>\n<h2>EventListenerMethodProcessor<\/h2>\n<p>org\/springframework\/context\/event\/EventListenerMethodProcessor.java<\/p>\n<pre><code>public class EventListenerMethodProcessor\n        implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {\n\n    \/\/......\n\n    @Override\n    public void afterSingletonsInstantiated() {\n        ConfigurableListableBeanFactory beanFactory = this.beanFactory;\n        Assert.state(this.beanFactory != null, &quot;No ConfigurableListableBeanFactory set&quot;);\n        String[] beanNames = beanFactory.getBeanNamesForType(Object.class);\n        for (String beanName : beanNames) {\n            if (!ScopedProxyUtils.isScopedTarget(beanName)) {\n                Class&lt;?&gt; type = null;\n                try {\n                    type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);\n                }\n                catch (Throwable ex) {\n                    \/\/ An unresolvable bean type, probably from a lazy bean - let's ignore it.\n                    if (logger.isDebugEnabled()) {\n                        logger.debug(&quot;Could not resolve target class for bean with name '&quot; + beanName + &quot;'&quot;, ex);\n                    }\n                }\n                if (type != null) {\n                    if (ScopedObject.class.isAssignableFrom(type)) {\n                        try {\n                            Class&lt;?&gt; targetClass = AutoProxyUtils.determineTargetClass(\n                                    beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));\n                            if (targetClass != null) {\n                                type = targetClass;\n                            }\n                        }\n                        catch (Throwable ex) {\n                            \/\/ An invalid scoped proxy arrangement - let's ignore it.\n                            if (logger.isDebugEnabled()) {\n                                logger.debug(&quot;Could not resolve target bean for scoped proxy '&quot; + beanName + &quot;'&quot;, ex);\n                            }\n                        }\n                    }\n                    try {\n                        processBean(beanName, type);\n                    }\n                    catch (Throwable ex) {\n                        throw new BeanInitializationException(&quot;Failed to process @EventListener &quot; +\n                                &quot;annotation on bean with name '&quot; + beanName + &quot;'&quot;, ex);\n                    }\n                }\n            }\n        }\n    }\n\n    private void processBean(final String beanName, final Class&lt;?&gt; targetType) {\n        if (!this.nonAnnotatedClasses.contains(targetType) &amp;&amp;\n                AnnotationUtils.isCandidateClass(targetType, EventListener.class) &amp;&amp;\n                !isSpringContainerClass(targetType)) {\n\n            Map&lt;Method, EventListener&gt; annotatedMethods = null;\n            try {\n                annotatedMethods = MethodIntrospector.selectMethods(targetType,\n                        (MethodIntrospector.MetadataLookup&lt;EventListener&gt;) method -&gt;\n                                AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));\n            }\n            catch (Throwable ex) {\n                \/\/ An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.\n                if (logger.isDebugEnabled()) {\n                    logger.debug(&quot;Could not resolve methods for bean with name '&quot; + beanName + &quot;'&quot;, ex);\n                }\n            }\n\n            if (CollectionUtils.isEmpty(annotatedMethods)) {\n                this.nonAnnotatedClasses.add(targetType);\n                if (logger.isTraceEnabled()) {\n                    logger.trace(&quot;No @EventListener annotations found on bean class: &quot; + targetType.getName());\n                }\n            }\n            else {\n                \/\/ Non-empty set of methods\n                ConfigurableApplicationContext context = this.applicationContext;\n                Assert.state(context != null, &quot;No ApplicationContext set&quot;);\n                List&lt;EventListenerFactory&gt; factories = this.eventListenerFactories;\n                Assert.state(factories != null, &quot;EventListenerFactory List not initialized&quot;);\n                for (Method method : annotatedMethods.keySet()) {\n                    for (EventListenerFactory factory : factories) {\n                        if (factory.supportsMethod(method)) {\n                            Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));\n                            ApplicationListener&lt;?&gt; applicationListener =\n                                    factory.createApplicationListener(beanName, targetType, methodToUse);\n                            if (applicationListener instanceof ApplicationListenerMethodAdapter) {\n                                ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);\n                            }\n                            context.addApplicationListener(applicationListener);\n                            break;\n                        }\n                    }\n                }\n                if (logger.isDebugEnabled()) {\n                    logger.debug(annotatedMethods.size() + &quot; @EventListener methods processed on bean '&quot; +\n                            beanName + &quot;': &quot; + annotatedMethods);\n                }\n            }\n        }\n    }\n\n    \/\/......\n}        <\/code><\/pre>\n<blockquote><p>EventListenerMethodProcessor\u5b9e\u73b0\u4e86SmartInitializingSingleton\u63a5\u53e3\uff0c\u5176afterSingletonsInstantiated\u65b9\u6cd5\u5148\u786e\u5b9atype\uff0c\u7136\u540e\u6267\u884cprocessBean\uff0c\u8be5\u65b9\u6cd5\u4f1a\u5148\u6536\u96c6annotatedMethods\uff0c\u7136\u540e\u904d\u5386\u8be5\u65b9\u6cd5\uff0c\u5728\u904d\u5386factories\u9488\u5bf9\u652f\u6301\u8be5\u65b9\u6cd5\u7684factory\u6267\u884ccreateApplicationListener\uff0c\u6dfb\u52a0\u5230context\u4e2d<\/p><\/blockquote>\n<h2>\u5c0f\u7ed3<\/h2>\n<p>TransactionalEventListener\u662fEventListener\u7684\u4e8b\u52a1\u611f\u77e5\u7248\u672c\uff0c\u9ed8\u8ba4\u7684\u662fTransactionPhase\u662fAFTER_COMMIT\uff0cTransactionSynchronizationEventAdapter\u53ea\u662f\u8986\u76d6\u4e86beforeCommit\u53caafterCompletion\u4e24\u4e2a\u65b9\u6cd5\uff0c\u5728afterCompletion\u65b9\u6cd5\u4e2d\u6839\u636estatus\u7684\u503c\u4e0ephase\u7684\u503c\u7684\u5339\u914d\u5173\u7cfb\u51b3\u5b9a\u662f\u5426\u6267\u884cprocessEvent\uff0c\u56e0\u800c\u8fd9\u91cc\u629b\u51fa\u7684\u5f02\u5e38\u4f1a\u88ab\u6355\u83b7\u5e76log\u4e0b\u6765<\/p>\n<h2>doc<\/h2>\n<ul>\n<li><a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=R2VpmRMq4ZXXg4HQcduHmw%3D%3D.Tf5pT8jOIlU31nDzUHhuzlvC35MV4YQi15hcKM9u6akjM4bJV6C2DRUBKSZV0rCklypuwRCLAMNI6EEZvgbzyg%3D%3D\" rel=\"noopener noreferrer\">\u804a\u804aTransactionSynchronization\u7684invokeAfterCompletion<\/a><\/li>\n<li><a target=\"_blank\" href=\"https:\/\/link.segmentfault.com\/?enc=uPUYh59SrITi3%2FEsdO8xfA%3D%3D.RmofkrfCThOnaoJO3sr2c9B37Kw8vayWyMcDaiEXspBwLMmgzbAba5ZdJCzzPXLl7ShKe27stIZZCBtsTfH2ZA%3D%3D\" rel=\"noopener noreferrer\">\u804a\u804aspring\u7684TransactionSynchronizationAdapter<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/segmentfault.com\/a\/1190000044202722\">https:\/\/segmentfault.com\/a\/1190000044202722<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5e8f \u672c\u6587\u4e3b\u8981\u7814\u7a76\u4e00\u4e0bspring\u7684TransactionalEventListener Transaction [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-10008267","post","type-post","status-publish","format-standard","hentry","category-share"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10008267","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10008267"}],"version-history":[{"count":0,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10008267\/revisions"}],"wp:attachment":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10008267"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10008267"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10008267"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}