diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index c42f43ae48..22fd285427 100644
--- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -47,6 +47,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.HierarchicalMessageSource;
import org.springframework.context.LifecycleProcessor;
@@ -630,11 +631,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
+ beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
+ beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
- beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
@@ -643,6 +645,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
+ // Register early post-processor for detecting inner beans as ApplicationListeners.
+ beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
+
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
diff --git a/spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java b/spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java
new file mode 100644
index 0000000000..b18bc40ba1
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2002-2016 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.context.support;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
+import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ApplicationEventMulticaster;
+
+/**
+ * {@code BeanPostProcessor} that detects beans which implement the {@code ApplicationListener}
+ * interface. This catches beans that can't reliably be detected by {@code getBeanNamesForType}
+ * and related operations which only work against top-level beans.
+ *
+ *
With standard Java serialization, this post-processor won't get serialized as part of
+ * {@code DisposableBeanAdapter} to begin with. However, with alternative serialization
+ * mechanisms, {@code DisposableBeanAdapter.writeReplace} might not get used at all, so we
+ * defensively mark this post-processor's field state as {@code transient}.
+ *
+ * @author Juergen Hoeller
+ * @since 4.3.4
+ */
+class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
+
+ private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class);
+
+ private transient final AbstractApplicationContext applicationContext;
+
+ private transient final Map singletonNames = new ConcurrentHashMap<>(256);
+
+
+ public ApplicationListenerDetector(AbstractApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+
+ @Override
+ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
+ if (this.applicationContext != null && beanDefinition.isSingleton()) {
+ this.singletonNames.put(beanName, Boolean.TRUE);
+ }
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) {
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) {
+ if (this.applicationContext != null && bean instanceof ApplicationListener) {
+ // potentially not detected as a listener by getBeanNamesForType retrieval
+ Boolean flag = this.singletonNames.get(beanName);
+ if (Boolean.TRUE.equals(flag)) {
+ // singleton bean (top-level or inner): register on the fly
+ this.applicationContext.addApplicationListener((ApplicationListener>) bean);
+ }
+ else if (flag == null) {
+ if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
+ // inner bean with other scope - can't reliably process events
+ logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
+ "but is not reachable for event multicasting by its containing ApplicationContext " +
+ "because it does not have singleton scope. Only top-level listener beans are allowed " +
+ "to be of non-singleton scope.");
+ }
+ this.singletonNames.put(beanName, Boolean.FALSE);
+ }
+ }
+ return bean;
+ }
+
+ @Override
+ public void postProcessBeforeDestruction(Object bean, String beanName) {
+ if (bean instanceof ApplicationListener) {
+ ApplicationEventMulticaster multicaster = this.applicationContext.getApplicationEventMulticaster();
+ multicaster.removeApplicationListener((ApplicationListener>) bean);
+ multicaster.removeApplicationListenerBean(beanName);
+ }
+ }
+
+ @Override
+ public boolean requiresDestruction(Object bean) {
+ return (bean instanceof ApplicationListener);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ return (this == other || (other instanceof ApplicationListenerDetector &&
+ this.applicationContext == ((ApplicationListenerDetector) other).applicationContext));
+ }
+
+ @Override
+ public int hashCode() {
+ return this.applicationContext.hashCode();
+ }
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java
index bcd55639f0..e3d6c6fc88 100644
--- a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java
+++ b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java
@@ -23,9 +23,7 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -34,14 +32,11 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
@@ -249,6 +244,8 @@ class PostProcessorRegistrationDelegate {
sortPostProcessors(beanFactory, internalPostProcessors);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
+ // Re-register post-processor for detecting inner beans as ApplicationListeners,
+ // moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
@@ -342,78 +339,4 @@ class PostProcessorRegistrationDelegate {
}
}
-
- /**
- * {@code BeanPostProcessor} that detects beans which implement the {@code ApplicationListener}
- * interface. This catches beans that can't reliably be detected by {@code getBeanNamesForType}
- * and related operations which only work against top-level beans.
- *
- * With standard Java serialization, this post-processor won't get serialized as part of
- * {@code DisposableBeanAdapter} to begin with. However, with alternative serialization
- * mechanisms, {@code DisposableBeanAdapter.writeReplace} might not get used at all, so we
- * defensively mark this post-processor's field state as {@code transient}.
- */
- private static class ApplicationListenerDetector
- implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
-
- private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class);
-
- private transient final AbstractApplicationContext applicationContext;
-
- private transient final Map singletonNames = new ConcurrentHashMap<>(256);
-
- public ApplicationListenerDetector(AbstractApplicationContext applicationContext) {
- this.applicationContext = applicationContext;
- }
-
- @Override
- public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
- if (this.applicationContext != null && beanDefinition.isSingleton()) {
- this.singletonNames.put(beanName, Boolean.TRUE);
- }
- }
-
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) {
- return bean;
- }
-
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) {
- if (this.applicationContext != null && bean instanceof ApplicationListener) {
- // potentially not detected as a listener by getBeanNamesForType retrieval
- Boolean flag = this.singletonNames.get(beanName);
- if (Boolean.TRUE.equals(flag)) {
- // singleton bean (top-level or inner): register on the fly
- this.applicationContext.addApplicationListener((ApplicationListener>) bean);
- }
- else if (flag == null) {
- if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
- // inner bean with other scope - can't reliably process events
- logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
- "but is not reachable for event multicasting by its containing ApplicationContext " +
- "because it does not have singleton scope. Only top-level listener beans are allowed " +
- "to be of non-singleton scope.");
- }
- this.singletonNames.put(beanName, Boolean.FALSE);
- }
- }
- return bean;
- }
-
- @Override
- public void postProcessBeforeDestruction(Object bean, String beanName) {
- if (bean instanceof ApplicationListener) {
- ApplicationEventMulticaster multicaster = this.applicationContext.getApplicationEventMulticaster();
- multicaster.removeApplicationListener((ApplicationListener>) bean);
- multicaster.removeApplicationListenerBean(beanName);
- }
- }
-
- @Override
- public boolean requiresDestruction(Object bean) {
- return (bean instanceof ApplicationListener);
- }
- }
-
}
diff --git a/spring-context/src/test/java/org/springframework/context/support/BeanFactoryPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/support/BeanFactoryPostProcessorTests.java
index b68aef069d..1c9d437f03 100644
--- a/spring-context/src/test/java/org/springframework/context/support/BeanFactoryPostProcessorTests.java
+++ b/spring-context/src/test/java/org/springframework/context/support/BeanFactoryPostProcessorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -28,8 +28,12 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProce
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.PriorityOrdered;
import org.springframework.tests.sample.beans.TestBean;
+import org.springframework.util.Assert;
import static org.junit.Assert.*;
@@ -119,6 +123,24 @@ public class BeanFactoryPostProcessorTests {
assertTrue(ac.getBean(TestBeanFactoryPostProcessor.class).wasCalled);
}
+ @Test
+ public void testBeanFactoryPostProcessorAsApplicationListener() {
+ StaticApplicationContext ac = new StaticApplicationContext();
+ ac.registerBeanDefinition("bfpp", new RootBeanDefinition(ListeningBeanFactoryPostProcessor.class));
+ ac.refresh();
+ assertTrue(ac.getBean(ListeningBeanFactoryPostProcessor.class).received instanceof ContextRefreshedEvent);
+ }
+
+ @Test
+ public void testBeanFactoryPostProcessorWithInnerBeanAsApplicationListener() {
+ StaticApplicationContext ac = new StaticApplicationContext();
+ RootBeanDefinition rbd = new RootBeanDefinition(NestingBeanFactoryPostProcessor.class);
+ rbd.getPropertyValues().add("listeningBean", new RootBeanDefinition(ListeningBean.class));
+ ac.registerBeanDefinition("bfpp", rbd);
+ ac.refresh();
+ assertTrue(ac.getBean(NestingBeanFactoryPostProcessor.class).getListeningBean().received instanceof ContextRefreshedEvent);
+ }
+
public static class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@@ -170,4 +192,50 @@ public class BeanFactoryPostProcessorTests {
}
}
+
+ public static class ListeningBeanFactoryPostProcessor implements BeanFactoryPostProcessor, ApplicationListener {
+
+ public ApplicationEvent received;
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ }
+
+ @Override
+ public void onApplicationEvent(ApplicationEvent event) {
+ Assert.state(this.received == null, "Just one ContextRefreshedEvent expected");
+ this.received = event;
+ }
+ }
+
+
+ public static class ListeningBean implements ApplicationListener {
+
+ public ApplicationEvent received;
+
+ @Override
+ public void onApplicationEvent(ApplicationEvent event) {
+ Assert.state(this.received == null, "Just one ContextRefreshedEvent expected");
+ this.received = event;
+ }
+ }
+
+
+ public static class NestingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
+
+ private ListeningBean listeningBean;
+
+ public void setListeningBean(ListeningBean listeningBean) {
+ this.listeningBean = listeningBean;
+ }
+
+ public ListeningBean getListeningBean() {
+ return listeningBean;
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ }
+ }
+
}