软件版本
- Spring版本5.3.18及更早的
- JDK版本1.8.0_202
当
我使用Spring ApplicationListener时,为了防止交易无效,我的ApplicationListener实现类别都会写下以下代码(当然,代码可以避免此问题以避免此问题),这将使我的听众在发布事件后两次触发。我认为这不是正常的,但不确定是否是错误,所以我想问每个人的意见。
@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
@Autowired
DemoService1 demoService1;
@Autowired
DemoService2 demoService2;
@Autowired
EventDemoListener eventDemoListener;
@Override
public void onApplicationEvent(EventDemo event) {
eventDemoListener.testTransaction();
System.out.println("receiver " + event.getMessage());
}
@Transactional(rollbackFor = Exception.class)
public void testTransaction() {
demoService1.doService();
demoService2.doService();
}
}
通过这个演示项目,可以复制此问题。运行前请阅读readme.md文档。
https://github.com/zifeng.com/zifeng-wu/spring-study-spring-study
分析
分析后,因为在此创建 EventDemolistener
时,属性填充将触发 defaultsingletonBeanRegistry#getsingleton(string,boolean)
提前。
。。
-
然后 singletonFactory.getObject()
在 getsingleton()()
中执行的会导致unvroxyed eventDemolistener
将其放入 Abstract> Abstract> Abstractautoproproxycreator#中早期的二元重点
。
-
填充属性后,调用 AbstractAutowireCapableBeanFactory#initializeBean(字符串,对象,rootbeanDefinition)
并执行 application> applicationListenerDetector#postprocessafterInitialization(object,code,string)
将导致 code EventDemolistener
要将对象放入 AbstractApplicationEventMulticaster.defaultListenerReTriever#ApplicationListeners
Container。
-
然后,当事件发布后,执行 AbstractApplicationEventMulticaster.defaultListEnerReTriever#getApplicationListeners()
并使用 ApplicationListElistener&lt;?&gt;?&gt;?侦听器= beanfactory.getBean(listererbeanname,applicationListener.class)
要获得侦听器是代理 eventDemolistener
object。
-
目前,仅 eventDemolistener
在 application -applicationListeners
容器中的对象,因此代理 eventDemolistener
将添加到最终如下图所示,返回的AllListeners Collection,最终将导致侦听器两次触发。
Software versions
- Spring Version 5.3.18 and earlier
- JDK Version 1.8.0_202
Overview
When I use Spring ApplicationListener, in order to prevent transaction invalidation, my ApplicationListener implementation class writes the following code (of course, the code can be written differently to avoid this problem), which will cause my listener to trigger twice after the event is published. I think it's not normal, but not sure if it's a bug, so I want to ask everyone's opinion.
@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
@Autowired
DemoService1 demoService1;
@Autowired
DemoService2 demoService2;
@Autowired
EventDemoListener eventDemoListener;
@Override
public void onApplicationEvent(EventDemo event) {
eventDemoListener.testTransaction();
System.out.println("receiver " + event.getMessage());
}
@Transactional(rollbackFor = Exception.class)
public void testTransaction() {
demoService1.doService();
demoService2.doService();
}
}
Through this demo project, this problem can be reproduced. Please read the README.md document before running.
https://github.com/ZiFeng-Wu/spring-study
Analysis
-
After analysis, because here DI itself , When EventDemoListener
is created, property filling will trigger DefaultSingletonBeanRegistry#getSingleton(String, boolean)
in advance.
-
Then singletonFactory.getObject()
executed in getSingleton()
will cause the unproxyed EventDemoListener
object to be put into AbstractAutoProxyCreator#earlyProxyReferences
.
-
After the properties are filled, calling AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)
and executing ApplicationListenerDetector#postProcessAfterInitialization(Object, String)
will cause the unproxyed EventDemoListener
object to be put into the AbstractApplicationEventMulticaster.DefaultListenerRetriever#applicationListeners
container.

-
Then when the event is published, execute AbstractApplicationEventMulticaster.DefaultListenerRetriever#getApplicationListeners()
and use ApplicationListener<?> listener =beanFactory.getBean(listenerBeanName, ApplicationListener.class)
to obtain the listener is the proxied EventDemoListener
object.
-
At this time, there are only unproxyed EventDemoListener
object in the applicationListeners
container, so the proxied EventDemoListener
object will be added to the final returned allListeners collection, as shown in the figure below, which will eventually cause the listener to be triggered twice.

发布评论
评论(1)
更新的答案
现在,通过您更新的 GitHub 项目,我可以重现该问题。当使用针对侦听器类的 Spring AOP 方面时也会发生这种情况,而不仅仅是在自注入 + @Transactional 的特殊情况下。 IMO,这是一个 Spring Core 错误,这就是我创建 PR #28322< 的原因/a> 以修复 问题#28283 您在此处交叉发布之前或之后提出了要求。您应该在问题中链接到该问题,我刚刚找到它是因为我在自己创建问题之前正在搜索关键字。
另请参阅我在该问题中的评论,从 this one。
原始答案(供参考)
好的,在你的主类中我更改
为
现在应用程序启动,也没有数据库配置。它只是打印:
没有例外。也就是说,如果它对您不起作用,则可能是您的 XML 配置中存在错误。但实际上,您确实不需要它,因为您已经使用了组件和服务注释。
因此,如果我需要数据库设置来重现此问题,请像我在评论中所说的那样,更新项目以提供开箱即用的 H2 配置。
Updated answer
Now with your updated GitHub project, I can reproduce the problem. It also occurs when using a Spring AOP aspect targeting the listener class, not just in the special case of self-injection +
@Transactional
. IMO, it is a Spring Core bug, which is why I created PR #28322 in order to fix the issue #28283 you raised either before or after cross-posting here. You should have linked to that issue in your question, I just found it because I was searching for key words before creating an issue for it myself.See also my comment in the issue, starting with this one.
Original answer (for reference)
OK, in your main class I changed
to
Now the application starts, also without DB configuration. It simply prints:
There is no exception. I.e., if for you it does not work, probably there is a bug in your XML configuration. But actually, you really do not need it, because you used component and service annotations already.
So if I need a database setup in order to reproduce this, please, like I said in my comment, update the project to provide an H2 configuration which works out of the box.