Skip to content

Commit 86d52a6

Browse files
kriegaexjhoeller
authored andcommitted
Regression test for duplicate firing of proxied application listeners
ApplicationContextEventTests.eventForSelfInjectedProxiedListenerFiredOnlyOnce relates to and reproduces #28283.
1 parent 299a10c commit 86d52a6

File tree

6 files changed

+102
-0
lines changed

6 files changed

+102
-0
lines changed

spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
import org.springframework.context.ApplicationEventPublisherAware;
4141
import org.springframework.context.ApplicationListener;
4242
import org.springframework.context.PayloadApplicationEvent;
43+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
44+
import org.springframework.context.event.test.self_inject.MyApplication;
45+
import org.springframework.context.event.test.self_inject.MyEventListener;
46+
import org.springframework.context.event.test.self_inject.MyEventPublisher;
47+
import org.springframework.context.support.AbstractApplicationContext;
4348
import org.springframework.context.support.GenericApplicationContext;
4449
import org.springframework.context.support.StaticApplicationContext;
4550
import org.springframework.context.support.StaticMessageSource;
@@ -271,6 +276,24 @@ public void proxiedListenersMixedWithTargetListeners() {
271276
assertThat(listener1.seenEvents).hasSize(2);
272277
}
273278

279+
/**
280+
* Regression test for <a href="https://github.com/spring-projects/spring-framework/issues/28283">issue 28283</a>,
281+
* where event listeners proxied due to e.g.
282+
* <ul>
283+
* <li>{@code @Transactional} annotations in their methods or</li>
284+
* <li>being targeted by aspects</li>
285+
* </ul>
286+
* were added to the list of application listener beans twice (both proxy and unwrapped target).
287+
*/
288+
@Test
289+
public void eventForSelfInjectedProxiedListenerFiredOnlyOnce() {
290+
String basePackage = MyApplication.class.getPackageName();
291+
AbstractApplicationContext context = new AnnotationConfigApplicationContext(basePackage);
292+
context.getBean(MyEventPublisher.class).publishMyEvent("hello");
293+
assertThat(MyEventListener.eventCount).isEqualTo(1);
294+
context.close();
295+
}
296+
274297
@Test
275298
public void testEventPublicationInterceptor() throws Throwable {
276299
MethodInvocation invocation = mock();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.springframework.context.event.test.self_inject;
2+
3+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.context.annotation.EnableAspectJAutoProxy;
6+
import org.springframework.context.support.AbstractApplicationContext;
7+
8+
@Configuration
9+
@EnableAspectJAutoProxy(proxyTargetClass = true)
10+
public class MyApplication {
11+
public static void main(String[] args) {
12+
try (AbstractApplicationContext context = new AnnotationConfigApplicationContext("org.springframework.context.event.test.self_inject")) {
13+
context.getBean(MyEventPublisher.class).publishMyEvent("hello");
14+
assert MyEventListener.eventCount == 1 : "event listener must fire exactly once";
15+
}
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.springframework.context.event.test.self_inject;
2+
3+
import org.aspectj.lang.JoinPoint;
4+
import org.aspectj.lang.annotation.Aspect;
5+
import org.aspectj.lang.annotation.Before;
6+
import org.springframework.stereotype.Component;
7+
8+
@Aspect
9+
@Component
10+
public class MyAspect {
11+
@Before("within(org.springframework.context.event.test.self_inject.MyEventListener)")
12+
public void myAdvice(JoinPoint joinPoint) {
13+
//System.out.println(joinPoint);
14+
}
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.springframework.context.event.test.self_inject;
2+
3+
import org.springframework.context.ApplicationEvent;
4+
5+
public class MyEvent extends ApplicationEvent {
6+
private String message;
7+
8+
public MyEvent(Object source, String message) {
9+
super(source);
10+
this.message = message;
11+
}
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.springframework.context.event.test.self_inject;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.context.ApplicationListener;
5+
import org.springframework.stereotype.Component;
6+
7+
@Component
8+
public class MyEventListener implements ApplicationListener<MyEvent> {
9+
public static int eventCount;
10+
11+
@Autowired // use '-Dspring.main.allow-circular-references=true' in Spring Boot >= 2.6.0
12+
//@Lazy // with '@Lazy', the problem does not occur
13+
private MyEventListener eventDemoListener;
14+
15+
@Override
16+
public void onApplicationEvent(MyEvent event) {
17+
//System.out.println("Event: " + event);
18+
eventCount++;
19+
}
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.springframework.context.event.test.self_inject;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.context.ApplicationEventPublisher;
5+
import org.springframework.stereotype.Component;
6+
7+
@Component
8+
public class MyEventPublisher {
9+
@Autowired
10+
private ApplicationEventPublisher eventPublisher;
11+
12+
public void publishMyEvent(String message) {
13+
eventPublisher.publishEvent(new MyEvent(this, message));
14+
}
15+
}

0 commit comments

Comments
 (0)