From 86d52a677a41caa70477725d764c3deb2736bcb0 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Wed, 19 Jul 2023 03:45:45 +0200 Subject: [PATCH] Regression test for duplicate firing of proxied application listeners ApplicationContextEventTests.eventForSelfInjectedProxiedListenerFiredOnlyOnce relates to and reproduces #28283. --- .../event/ApplicationContextEventTests.java | 23 +++++++++++++++++++ .../event/test/self_inject/MyApplication.java | 17 ++++++++++++++ .../event/test/self_inject/MyAspect.java | 15 ++++++++++++ .../event/test/self_inject/MyEvent.java | 12 ++++++++++ .../test/self_inject/MyEventListener.java | 20 ++++++++++++++++ .../test/self_inject/MyEventPublisher.java | 15 ++++++++++++ 6 files changed, 102 insertions(+) create mode 100644 spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyApplication.java create mode 100644 spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyAspect.java create mode 100644 spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEvent.java create mode 100644 spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventListener.java create mode 100644 spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventPublisher.java diff --git a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java index bb5c0526906..a7f6676a000 100644 --- a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java @@ -40,6 +40,11 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationListener; import org.springframework.context.PayloadApplicationEvent; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.event.test.self_inject.MyApplication; +import org.springframework.context.event.test.self_inject.MyEventListener; +import org.springframework.context.event.test.self_inject.MyEventPublisher; +import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.StaticApplicationContext; import org.springframework.context.support.StaticMessageSource; @@ -271,6 +276,24 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen assertThat(listener1.seenEvents).hasSize(2); } + /** + * Regression test for issue 28283, + * where event listeners proxied due to e.g. + * + * were added to the list of application listener beans twice (both proxy and unwrapped target). + */ + @Test + public void eventForSelfInjectedProxiedListenerFiredOnlyOnce() { + String basePackage = MyApplication.class.getPackageName(); + AbstractApplicationContext context = new AnnotationConfigApplicationContext(basePackage); + context.getBean(MyEventPublisher.class).publishMyEvent("hello"); + assertThat(MyEventListener.eventCount).isEqualTo(1); + context.close(); + } + @Test public void testEventPublicationInterceptor() throws Throwable { MethodInvocation invocation = mock(); diff --git a/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyApplication.java b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyApplication.java new file mode 100644 index 00000000000..6686c1b121b --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyApplication.java @@ -0,0 +1,17 @@ +package org.springframework.context.event.test.self_inject; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.support.AbstractApplicationContext; + +@Configuration +@EnableAspectJAutoProxy(proxyTargetClass = true) +public class MyApplication { + public static void main(String[] args) { + try (AbstractApplicationContext context = new AnnotationConfigApplicationContext("org.springframework.context.event.test.self_inject")) { + context.getBean(MyEventPublisher.class).publishMyEvent("hello"); + assert MyEventListener.eventCount == 1 : "event listener must fire exactly once"; + } + } +} diff --git a/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyAspect.java b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyAspect.java new file mode 100644 index 00000000000..9b037195a9e --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyAspect.java @@ -0,0 +1,15 @@ +package org.springframework.context.event.test.self_inject; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class MyAspect { + @Before("within(org.springframework.context.event.test.self_inject.MyEventListener)") + public void myAdvice(JoinPoint joinPoint) { + //System.out.println(joinPoint); + } +} diff --git a/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEvent.java b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEvent.java new file mode 100644 index 00000000000..2dc0f2bccf7 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEvent.java @@ -0,0 +1,12 @@ +package org.springframework.context.event.test.self_inject; + +import org.springframework.context.ApplicationEvent; + +public class MyEvent extends ApplicationEvent { + private String message; + + public MyEvent(Object source, String message) { + super(source); + this.message = message; + } +} diff --git a/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventListener.java b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventListener.java new file mode 100644 index 00000000000..5fddae77c05 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventListener.java @@ -0,0 +1,20 @@ +package org.springframework.context.event.test.self_inject; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Component +public class MyEventListener implements ApplicationListener { + public static int eventCount; + + @Autowired // use '-Dspring.main.allow-circular-references=true' in Spring Boot >= 2.6.0 + //@Lazy // with '@Lazy', the problem does not occur + private MyEventListener eventDemoListener; + + @Override + public void onApplicationEvent(MyEvent event) { + //System.out.println("Event: " + event); + eventCount++; + } +} diff --git a/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventPublisher.java b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventPublisher.java new file mode 100644 index 00000000000..0959134f7c8 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/event/test/self_inject/MyEventPublisher.java @@ -0,0 +1,15 @@ +package org.springframework.context.event.test.self_inject; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +@Component +public class MyEventPublisher { + @Autowired + private ApplicationEventPublisher eventPublisher; + + public void publishMyEvent(String message) { + eventPublisher.publishEvent(new MyEvent(this, message)); + } +}