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 32f99063893..28c37599776 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -16,6 +16,8 @@ package org.springframework.context.event; +import java.util.Map; + import org.springframework.aop.support.AopUtils; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; @@ -23,6 +25,7 @@ import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ConcurrentReferenceHashMap; /** * {@link GenericApplicationListener} adapter that determines supported event types @@ -35,6 +38,9 @@ import org.springframework.util.Assert; */ public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener { + private static final Map, ResolvableType> eventTypeCache = new ConcurrentReferenceHashMap<>(); + + private final ApplicationListener delegate; @Nullable @@ -86,17 +92,11 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE); } - @Nullable - static ResolvableType resolveDeclaredEventType(Class listenerType) { - ResolvableType resolvableType = ResolvableType.forClass(listenerType).as(ApplicationListener.class); - return (resolvableType.hasGenerics() ? resolvableType.getGeneric() : null); - } @Nullable private static ResolvableType resolveDeclaredEventType(ApplicationListener listener) { ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass()); - if (declaredEventType == null || declaredEventType.isAssignableFrom( - ResolvableType.forClass(ApplicationEvent.class))) { + if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) { Class targetClass = AopUtils.getTargetClass(listener); if (targetClass != listener.getClass()) { declaredEventType = resolveDeclaredEventType(targetClass); @@ -105,4 +105,14 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList return declaredEventType; } + @Nullable + static ResolvableType resolveDeclaredEventType(Class listenerType) { + ResolvableType eventType = eventTypeCache.get(listenerType); + if (eventType == null) { + eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric(); + eventTypeCache.put(listenerType, eventType); + } + return (eventType != ResolvableType.NONE ? eventType : null); + } + } diff --git a/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java index f7aedb13abf..8055ffc2161 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -33,13 +33,13 @@ public abstract class AbstractApplicationEventListenerTests { try { return ResolvableType.forField(TestEvents.class.getField(fieldName)); } - catch (NoSuchFieldException e) { + catch (NoSuchFieldException ex) { throw new IllegalStateException("No such field on Events '" + fieldName + "'"); } } - protected static class GenericTestEvent - extends ApplicationEvent { + + protected static class GenericTestEvent extends ApplicationEvent { private final T payload; @@ -51,11 +51,9 @@ public abstract class AbstractApplicationEventListenerTests { public T getPayload() { return this.payload; } - } - protected static class SmartGenericTestEvent - extends GenericTestEvent implements ResolvableTypeProvider { + protected static class SmartGenericTestEvent extends GenericTestEvent implements ResolvableTypeProvider { private final ResolvableType resolvableType; @@ -119,6 +117,7 @@ public abstract class AbstractApplicationEventListenerTests { @SuppressWarnings("rawtypes") static class RawApplicationListener implements ApplicationListener { + @Override public void onApplicationEvent(ApplicationEvent event) { } @@ -137,7 +136,6 @@ public abstract class AbstractApplicationEventListenerTests { public GenericTestEvent illegalStateExceptionEvent; public GenericTestEvent ioExceptionEvent; - } } 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 f7fb80595fc..41d3e254e7f 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 @@ -63,6 +63,8 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen @Test public void multicastSimpleEvent() { + multicastEvent(true, ApplicationListener.class, + new ContextRefreshedEvent(new StaticApplicationContext()), null); multicastEvent(true, ApplicationListener.class, new ContextClosedEvent(new StaticApplicationContext()), null); } diff --git a/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java b/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java index c694c181daa..6ae033a318d 100644 --- a/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 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. @@ -100,8 +100,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE @Test public void genericListenerStrictTypeSubClass() { - supportsEventType(false, ObjectEventListener.class, - getGenericApplicationEventType("longEvent")); + supportsEventType(false, ObjectEventListener.class, getGenericApplicationEventType("longEvent")); } @Test @@ -111,7 +110,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE } @Test - public void genericListenerUpperBoundTypeNotMatching() throws NoSuchFieldException { + public void genericListenerUpperBoundTypeNotMatching() { supportsEventType(false, UpperBoundEventListener.class, getGenericApplicationEventType("ioExceptionEvent")); } @@ -142,8 +141,9 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE supportsEventType(true, RawApplicationListener.class, eventType); } - private void supportsEventType(boolean match, Class listenerType, - ResolvableType eventType) { + + private void supportsEventType( + boolean match, Class listenerType, ResolvableType eventType) { ApplicationListener listener = mock(listenerType); GenericApplicationListenerAdapter adapter = new GenericApplicationListenerAdapter(listener);