提交 daf29118 编写于 作者: J Juergen Hoeller

Resolve ApplicationListener against BeanDefinition.getResolvableType()

This covers ApplicationListener generics in factory method return types in particular but also allows for programmatic setTargetType hints.

Closes gh-23178
上级 8aa0b077
......@@ -19,6 +19,7 @@ package org.springframework.beans.factory.config;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
/**
......@@ -304,6 +305,17 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// Read-only attributes
/**
* Return a resolvable type for this bean definition,
* based on the bean class or other specific metadata.
* <p>This is typically fully resolved on a runtime-merged bean definition
* but not necessarily on a configuration-time definition instance.
* @return the resolvable type (potentially {@link ResolvableType#NONE})
* @since 5.2
* @see ConfigurableBeanFactory#getMergedBeanDefinition
*/
ResolvableType getResolvableType();
/**
* Return whether this a <b>Singleton</b>, with a single, shared instance
* returned on all calls.
......
......@@ -29,6 +29,7 @@ import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
......@@ -458,6 +459,16 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
return resolvedClass;
}
/**
* Return a resolvable type for this bean definition.
* <p>This implementation delegates to {@link #getBeanClass()}.
* @since 5.2
*/
@Override
public ResolvableType getResolvableType() {
return (hasBeanClass() ? ResolvableType.forClass(getBeanClass()) : ResolvableType.NONE);
}
/**
* Set the name of the target scope for the bean.
* <p>The default is singleton status, although this is only applied once
......
......@@ -331,14 +331,28 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
/**
* Return a {@link ResolvableType} for this bean definition,
* either from runtime-cached type information or from configuration-time
* {@link #setTargetType(ResolvableType)} or {@link #setBeanClass(Class)}.
* {@link #setTargetType(ResolvableType)} or {@link #setBeanClass(Class)},
* also considering resolved factory method definitions.
* @since 5.1
* @see #getTargetType()
* @see #getBeanClass()
* @see #setTargetType(ResolvableType)
* @see #setBeanClass(Class)
* @see #setResolvedFactoryMethod(Method)
*/
@Override
public ResolvableType getResolvableType() {
ResolvableType targetType = this.targetType;
return (targetType != null ? targetType : ResolvableType.forClass(getBeanClass()));
if (targetType != null) {
return targetType;
}
ResolvableType returnType = this.factoryMethodReturnType;
if (returnType != null) {
return returnType;
}
Method factoryMethod = this.factoryMethodToIntrospect;
if (factoryMethod != null) {
return ResolvableType.forMethodReturnType(factoryMethod);
}
return super.getResolvableType();
}
/**
......
......@@ -29,6 +29,7 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
......@@ -70,7 +71,7 @@ public abstract class AbstractApplicationEventMulticaster
private ClassLoader beanClassLoader;
@Nullable
private BeanFactory beanFactory;
private ConfigurableBeanFactory beanFactory;
private Object retrievalMutex = this.defaultRetriever;
......@@ -82,17 +83,17 @@ public abstract class AbstractApplicationEventMulticaster
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
if (beanFactory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
if (this.beanClassLoader == null) {
this.beanClassLoader = cbf.getBeanClassLoader();
}
this.retrievalMutex = cbf.getSingletonMutex();
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
}
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
if (this.beanClassLoader == null) {
this.beanClassLoader = this.beanFactory.getBeanClassLoader();
}
this.retrievalMutex = this.beanFactory.getSingletonMutex();
}
private BeanFactory getBeanFactory() {
private ConfigurableBeanFactory getBeanFactory() {
if (this.beanFactory == null) {
throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans " +
"because it is not associated with a BeanFactory");
......@@ -221,6 +222,9 @@ public abstract class AbstractApplicationEventMulticaster
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
// Add programmatically registered listeners, including ones coming
// from ApplicationListenerDetector (singleton beans and inner beans).
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
......@@ -229,12 +233,14 @@ public abstract class AbstractApplicationEventMulticaster
allListeners.add(listener);
}
}
// Add listeners by bean name, potentially overlapping with programmatically
// registered listeners above - but here potentially with additional metadata.
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
ConfigurableBeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, eventType)) {
if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
......@@ -249,6 +255,16 @@ public abstract class AbstractApplicationEventMulticaster
allListeners.add(listener);
}
}
else {
// Remove non-matching listeners that originally came from
// ApplicationListenerDetector, possibly ruled out by additional
// BeanDefinition metadata (e.g. factory method generics) above.
Object listener = beanFactory.getSingleton(listenerBeanName);
if (retriever != null) {
retriever.applicationListeners.remove(listener);
}
allListeners.remove(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
......@@ -256,6 +272,7 @@ public abstract class AbstractApplicationEventMulticaster
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
......@@ -264,6 +281,42 @@ public abstract class AbstractApplicationEventMulticaster
return allListeners;
}
/**
* Filter a bean-defined listener early through checking its generically declared
* event type before trying to instantiate it.
* <p>If this method returns {@code true} for a given listener as a first pass,
* the listener instance will get retrieved and fully evaluated through a
* {@link #supportsEvent(ApplicationListener, ResolvableType, Class)} call afterwards.
* @param beanFactory the BeanFactory that contains the listener beans
* @param listenerBeanName the name of the bean in the BeanFactory
* @param eventType the event type to check
* @return whether the given listener should be included in the candidates
* for the given event type
* @see #supportsEvent(Class, ResolvableType)
* @see #supportsEvent(ApplicationListener, ResolvableType, Class)
*/
private boolean supportsEvent(
ConfigurableBeanFactory beanFactory, String listenerBeanName, ResolvableType eventType) {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || GenericApplicationListener.class.isAssignableFrom(listenerType) ||
SmartApplicationListener.class.isAssignableFrom(listenerType)) {
return true;
}
if (!supportsEvent(listenerType, eventType)) {
return false;
}
try {
BeanDefinition bd = beanFactory.getMergedBeanDefinition(listenerBeanName);
ResolvableType genericEventType = bd.getResolvableType().as(ApplicationListener.class).getGeneric();
return (genericEventType == ResolvableType.NONE || genericEventType.isAssignableFrom(eventType));
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore - no need to check resolvable type for manually registered singleton
return true;
}
}
/**
* Filter a listener early through checking its generically declared event
* type before trying to instantiate it.
......@@ -276,10 +329,6 @@ public abstract class AbstractApplicationEventMulticaster
* for the given event type
*/
protected boolean supportsEvent(Class<?> listenerType, ResolvableType eventType) {
if (GenericApplicationListener.class.isAssignableFrom(listenerType) ||
SmartApplicationListener.class.isAssignableFrom(listenerType)) {
return true;
}
ResolvableType declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType);
return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));
}
......
......@@ -52,6 +52,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.annotation.Scope;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.tests.sample.beans.ITestBean;
......@@ -273,6 +274,18 @@ public class ConfigurationClassProcessingTests {
assertThat(ctx.getBean(NestedTestBean.class).getCompany()).isEqualTo("functional");
}
@Test
public void configurationWithApplicationListener() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConfigWithApplicationListener.class);
ctx.refresh();
ConfigWithApplicationListener config = ctx.getBean(ConfigWithApplicationListener.class);
assertThat(config.closed).isFalse();
ctx.close();
assertThat(config.closed).isTrue();
}
/**
* Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition}
......@@ -567,4 +580,16 @@ public class ConfigurationClassProcessingTests {
}
}
@Configuration
static class ConfigWithApplicationListener {
boolean closed = false;
@Bean
public ApplicationListener<ContextClosedEvent> listener() {
return (event -> this.closed = true);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册