diff --git a/spring-context/src/main/java/org/springframework/context/ApplicationListener.java b/spring-context/src/main/java/org/springframework/context/ApplicationListener.java index 55254c5130..cb0891b0c7 100644 --- a/spring-context/src/main/java/org/springframework/context/ApplicationListener.java +++ b/spring-context/src/main/java/org/springframework/context/ApplicationListener.java @@ -18,7 +18,6 @@ package org.springframework.context; import java.util.EventListener; import java.util.function.Consumer; -import java.util.function.Predicate; /** * Interface to be implemented by application event listeners. @@ -36,6 +35,8 @@ import java.util.function.Predicate; * @param the specific {@code ApplicationEvent} subclass to listen to * @see org.springframework.context.ApplicationEvent * @see org.springframework.context.event.ApplicationEventMulticaster + * @see org.springframework.context.event.SmartApplicationListener + * @see org.springframework.context.event.GenericApplicationListener * @see org.springframework.context.event.EventListener */ @FunctionalInterface @@ -47,17 +48,6 @@ public interface ApplicationListener extends EventLi */ void onApplicationEvent(E event); - /** - * Return an optional identifier for the listener. - *

The default value is an empty String. - * @since 5.3.5 - * @see org.springframework.context.event.EventListener#id - * @see org.springframework.context.event.ApplicationEventMulticaster#removeApplicationListeners(Predicate) - */ - default String getListenerId() { - return ""; - } - /** * Create a new {@code ApplicationListener} for the given payload consumer. diff --git a/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java index 9a23a1e9c0..4fff846e8c 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java @@ -78,7 +78,7 @@ public interface ApplicationEventMulticaster { *

Note: This just applies to instance registrations, not to listeners * registered by bean name. * @param predicate the predicate to identify listener instances to remove, - * e.g. checking {@link ApplicationListener#getListenerId()} + * e.g. checking {@link SmartApplicationListener#getListenerId()} * @since 5.3.5 * @see #addApplicationListener(ApplicationListener) * @see #removeApplicationListener(ApplicationListener) diff --git a/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java index 0df8d3acb2..763f96f533 100644 --- a/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java +++ b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,7 @@ package org.springframework.context.event; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; -import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; -import org.springframework.lang.Nullable; /** * Extended variant of the standard {@link ApplicationListener} interface, @@ -28,13 +26,26 @@ import org.springframework.lang.Nullable; * *

As of Spring Framework 4.2, this interface supersedes the Class-based * {@link SmartApplicationListener} with full handling of generic event types. + * As of 5.3.5, it formally extends {@link SmartApplicationListener}, adapting + * {@link #supportsEventType(Class)} to {@link #supportsEventType(ResolvableType)} + * with a default method. * * @author Stephane Nicoll + * @author Juergen Hoeller * @since 4.2 * @see SmartApplicationListener * @see GenericApplicationListenerAdapter */ -public interface GenericApplicationListener extends ApplicationListener, Ordered { +public interface GenericApplicationListener extends SmartApplicationListener { + + /** + * Overrides {@link SmartApplicationListener#supportsEventType(Class)} with + * delegation to {@link #supportsEventType(ResolvableType)}. + */ + @Override + default boolean supportsEventType(Class eventType) { + return supportsEventType(ResolvableType.forClass(eventType)); + } /** * Determine whether this listener actually supports the given event type. @@ -42,22 +53,4 @@ public interface GenericApplicationListener extends ApplicationListenerThe default implementation always returns {@code true}. - * @param sourceType the source type, or {@code null} if no source - */ - default boolean supportsSourceType(@Nullable Class sourceType) { - return true; - } - - /** - * Determine this listener's order in a set of listeners for the same event. - *

The default implementation returns {@link #LOWEST_PRECEDENCE}. - */ - @Override - default int getOrder() { - return LOWEST_PRECEDENCE; - } - } diff --git a/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java index 42167e792d..b48352f0b6 100644 --- a/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java +++ b/spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java @@ -36,7 +36,7 @@ import org.springframework.util.ConcurrentReferenceHashMap; * @since 3.0 * @see org.springframework.context.ApplicationListener#onApplicationEvent */ -public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener { +public class GenericApplicationListenerAdapter implements GenericApplicationListener { private static final Map, ResolvableType> eventTypeCache = new ConcurrentReferenceHashMap<>(); @@ -64,15 +64,13 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList this.delegate.onApplicationEvent(event); } - @Override - public String getListenerId() { - return this.delegate.getListenerId(); - } - @Override @SuppressWarnings("unchecked") public boolean supportsEventType(ResolvableType eventType) { - if (this.delegate instanceof SmartApplicationListener) { + if (this.delegate instanceof GenericApplicationListener) { + return ((GenericApplicationListener) this.delegate).supportsEventType(eventType); + } + else if (this.delegate instanceof SmartApplicationListener) { Class eventClass = (Class) eventType.resolve(); return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass)); } @@ -81,11 +79,6 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList } } - @Override - public boolean supportsEventType(Class eventType) { - return supportsEventType(ResolvableType.forClass(eventType)); - } - @Override public boolean supportsSourceType(@Nullable Class sourceType) { return !(this.delegate instanceof SmartApplicationListener) || @@ -97,6 +90,12 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE); } + @Override + public String getListenerId() { + return (this.delegate instanceof SmartApplicationListener ? + ((SmartApplicationListener) this.delegate).getListenerId() : ""); + } + @Nullable private static ResolvableType resolveDeclaredEventType(ApplicationListener listener) { diff --git a/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java b/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java index 548b67f7aa..c56dd33b33 100644 --- a/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java +++ b/spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,4 +59,15 @@ public interface SmartApplicationListener extends ApplicationListenerThe default value is an empty String. + * @since 5.3.5 + * @see EventListener#id + * @see ApplicationEventMulticaster#removeApplicationListeners + */ + default String getListenerId() { + return ""; + } + } diff --git a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java index ea88fabf3a..a2593444ea 100644 --- a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java +++ b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java @@ -34,7 +34,7 @@ import org.springframework.lang.Nullable; * @author Stephane Nicoll * @since 2.0.5 */ -public class SourceFilteringListener implements GenericApplicationListener, SmartApplicationListener { +public class SourceFilteringListener implements GenericApplicationListener { private final Object source; @@ -74,21 +74,11 @@ public class SourceFilteringListener implements GenericApplicationListener, Smar } } - @Override - public String getListenerId() { - return (this.delegate != null ? this.delegate.getListenerId() : ""); - } - @Override public boolean supportsEventType(ResolvableType eventType) { return (this.delegate == null || this.delegate.supportsEventType(eventType)); } - @Override - public boolean supportsEventType(Class eventType) { - return supportsEventType(ResolvableType.forType(eventType)); - } - @Override public boolean supportsSourceType(@Nullable Class sourceType) { return (sourceType != null && sourceType.isInstance(this.source)); @@ -99,6 +89,11 @@ public class SourceFilteringListener implements GenericApplicationListener, Smar return (this.delegate != null ? this.delegate.getOrder() : Ordered.LOWEST_PRECEDENCE); } + @Override + public String getListenerId() { + return (this.delegate != null ? this.delegate.getListenerId() : ""); + } + /** * Actually process the event, after having filtered according to the diff --git a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java index a5fe786e6e..cd1e71a12d 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java @@ -112,8 +112,8 @@ public class AnnotationDrivenEventListenerTests { this.eventCollector.assertEvent(listener, event); this.eventCollector.assertTotalEventsCount(1); - context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners( - l -> l.getListenerId().contains("TestEvent")); + context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l -> + l instanceof SmartApplicationListener && ((SmartApplicationListener) l).getListenerId().contains("TestEvent")); this.eventCollector.clear(); this.context.publishEvent(event); this.eventCollector.assertNoEventReceived(listener); @@ -133,8 +133,8 @@ public class AnnotationDrivenEventListenerTests { this.eventCollector.assertEvent(listener, event); this.eventCollector.assertTotalEventsCount(1); - context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners( - l -> l.getListenerId().contains("TestEvent")); + context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l -> + l instanceof SmartApplicationListener && ((SmartApplicationListener) l).getListenerId().contains("TestEvent")); this.eventCollector.clear(); this.context.publishEvent(event); this.eventCollector.assertNoEventReceived(listener); @@ -151,8 +151,8 @@ public class AnnotationDrivenEventListenerTests { this.eventCollector.assertEvent(bean, event); this.eventCollector.assertTotalEventsCount(1); - context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners( - l -> l.getListenerId().equals("foo")); + context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l -> + l instanceof SmartApplicationListener && ((SmartApplicationListener) l).getListenerId().equals("foo")); this.eventCollector.clear(); this.context.publishEvent(event); this.eventCollector.assertNoEventReceived(bean); diff --git a/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListener.java b/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListener.java index c8c2fa5f7c..b90ed812ea 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListener.java +++ b/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListener.java @@ -64,11 +64,10 @@ public interface TransactionalApplicationListener *

It might be necessary for specific completion callback implementations * to provide a specific id, whereas for other scenarios an empty String * (as the common default value) is acceptable as well. - * @see ApplicationListener#getListenerId() + * @see org.springframework.context.event.SmartApplicationListener#getListenerId() * @see TransactionalEventListener#id * @see #addCallback */ - @Override default String getListenerId() { return ""; }