From 40ab6cecfc9cc36615bd9d9643d50ebce74aeafe Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 2 Nov 2023 14:27:52 +0100 Subject: [PATCH] GH-357 - Reinstantiate general compatibility with Boot 3.2 / Framework 6.1. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now gracefully fall back to reflective invocation of the application event listener shouldHandle(…) method if we're not on Spring Framework 6.2. If we are, we invoke the newly introduced method directly. This allows projects to upgrade to Spring Modulith 1.1 without necessarily upgrading to Boot 3.2. --- .github/workflows/integration.yaml | 2 + ...PersistentApplicationEventMulticaster.java | 38 +++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 35328cf9..5b8b09fb 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -10,6 +10,8 @@ jobs: strategy: matrix: version: + - '3.1.5' + - '3.1.6-SNAPSHOT' - '3.2.0-SNAPSHOT' name: Build against Boot ${{ matrix.version }} runs-on: ubuntu-latest diff --git a/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java b/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java index a1b47f14..020c4610 100644 --- a/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java +++ b/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java @@ -15,6 +15,7 @@ */ package org.springframework.modulith.events.support; +import java.lang.reflect.Method; import java.time.Duration; import java.util.Collection; import java.util.List; @@ -45,6 +46,7 @@ import org.springframework.modulith.events.core.TargetEventPublication; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalApplicationListener; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** * An {@link org.springframework.context.event.ApplicationEventMulticaster} to register {@link EventPublication}s in an @@ -61,12 +63,23 @@ public class PersistentApplicationEventMulticaster extends AbstractApplicationEv implements IncompleteEventPublications, SmartInitializingSingleton { private static final Logger LOGGER = LoggerFactory.getLogger(PersistentApplicationEventMulticaster.class); + private static final Method LEGACY_SHOULD_HANDLE = ReflectionUtils.findMethod(ApplicationListenerMethodAdapter.class, + "shouldHandle", ApplicationEvent.class, Object[].class); + private static final Method SHOULD_HANDLE = ReflectionUtils.findMethod(ApplicationListenerMethodAdapter.class, + "shouldHandle", ApplicationEvent.class); static final String REPUBLISH_ON_RESTART = "spring.modulith.republish-outstanding-events-on-restart"; private final @NonNull Supplier registry; private final @NonNull Supplier environment; + static { + + if (SHOULD_HANDLE == null) { + ReflectionUtils.makeAccessible(LEGACY_SHOULD_HANDLE); + } + } + /** * Creates a new {@link PersistentApplicationEventMulticaster} for the given {@link EventPublicationRegistry}. * @@ -222,9 +235,7 @@ public class PersistentApplicationEventMulticaster extends AbstractApplicationEv private static boolean matches(ApplicationEvent event, Object payload, ApplicationListener listener) { // Verify general listener matching by eagerly evaluating the condition - if (ApplicationListenerMethodAdapter.class.isInstance(listener) - && !((ApplicationListenerMethodAdapter) listener).shouldHandle(event)) { - + if (!invokeShouldHandle(listener, event, payload)) { return false; } @@ -233,6 +244,27 @@ public class PersistentApplicationEventMulticaster extends AbstractApplicationEv : true; } + /** + * Invokes {@link ApplicationListenerMethodAdapter#shouldHandle(ApplicationEvent)} in case the given candidate is one + * in the first place but falls back to call {@code shouldHandle(ApplicationEvent, Object)} reflectively as fallback. + * + * @param candidate the listener to test, must not be {@literal null}. + * @param event the event to publish, must not be {@literal null}. + * @param payload the actual payload, must not be {@literal null}. + * @return whether the event should be handled by the given candidate. + */ + @SuppressWarnings("null") + private static boolean invokeShouldHandle(ApplicationListener candidate, ApplicationEvent event, Object payload) { + + if (!(candidate instanceof ApplicationListenerMethodAdapter listener)) { + return true; + } + + return SHOULD_HANDLE != null + ? listener.shouldHandle(event) + : (boolean) ReflectionUtils.invokeMethod(LEGACY_SHOULD_HANDLE, candidate, event, new Object[] { payload }); + } + private static String getConfirmationMessage(Collection publications) { var size = publications.size();