diff --git a/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java b/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
index 025257fb5d0..d21653b2223 100644
--- a/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
+++ b/spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
@@ -18,7 +18,7 @@ package org.springframework.context;
/**
* Interface that encapsulates event publication functionality.
- * Serves as super-interface for ApplicationContext.
+ * Serves as super-interface for {@link ApplicationContext}.
*
* @author Juergen Hoeller
* @author Stephane Nicoll
@@ -42,9 +42,10 @@ public interface ApplicationEventPublisher {
/**
* Notify all matching listeners registered with this
* application of an event.
- *
If the specified {@code event} is not an {@link ApplicationEvent}, it
- * is wrapped in a {@code GenericApplicationEvent}.
+ *
If the specified {@code event} is not an {@link ApplicationEvent},
+ * it is wrapped in a {@link PayloadApplicationEvent}.
* @param event the event to publish
+ * @since 4.2
* @see PayloadApplicationEvent
*/
void publishEvent(Object event);
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 351563444e2..ed5f4c0b5bf 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
@@ -165,6 +165,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/** Parent context */
private ApplicationContext parent;
+ /** Environment used by this context */
+ private ConfigurableEnvironment environment;
+
/** BeanFactoryPostProcessors to apply on refresh */
private final List beanFactoryPostProcessors =
new ArrayList();
@@ -197,10 +200,10 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
private ApplicationEventMulticaster applicationEventMulticaster;
/** Statically specified listeners */
- private Set> applicationListeners = new LinkedHashSet>();
+ private final Set> applicationListeners = new LinkedHashSet>();
- /** Environment used by this context; initialized by {@link #createEnvironment()} */
- private ConfigurableEnvironment environment;
+ /** ApplicationEvents published early */
+ private Set earlyApplicationEvents;
/**
@@ -340,7 +343,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
- final ApplicationEvent applicationEvent;
+
+ // Decorate event as an ApplicationEvent if necessary
+ ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
@@ -350,7 +355,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
eventType = ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass());
}
}
- getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
+
+ // Multicast right now if possible - or lazily once the multicaster is initialized
+ if (this.earlyApplicationEvents != null) {
+ this.earlyApplicationEvents.add(applicationEvent);
+ }
+ else {
+ getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
+ }
+
+ // Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
@@ -379,7 +393,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* @return the internal LifecycleProcessor (never {@code null})
* @throws IllegalStateException if the context has not been initialized yet
*/
- LifecycleProcessor getLifecycleProcessor() {
+ LifecycleProcessor getLifecycleProcessor() throws IllegalStateException {
if (this.lifecycleProcessor == null) {
throw new IllegalStateException("LifecycleProcessor not initialized - " +
"call 'refresh' before invoking lifecycle methods via the context: " + this);
@@ -543,6 +557,10 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
+
+ // Allow for the collection of early ApplicationEvents,
+ // to be published once the multicaster is available...
+ this.earlyApplicationEvents = new LinkedHashSet();
}
/**
@@ -748,11 +766,21 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
for (ApplicationListener> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
+
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
- for (String lisName : listenerBeanNames) {
- getApplicationEventMulticaster().addApplicationListenerBean(lisName);
+ for (String listenerBeanName : listenerBeanNames) {
+ getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
+ }
+
+ // Publish early application events now that we finally have a multicaster...
+ Set earlyEventsToProcess = this.earlyApplicationEvents;
+ this.earlyApplicationEvents = null;
+ if (earlyEventsToProcess != null) {
+ for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
+ getApplicationEventMulticaster().multicastEvent(earlyEvent);
+ }
}
}
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 13174ea6b9b..e7651dc36f6 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
@@ -24,15 +24,20 @@ import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.BeanThatBroadcasts;
import org.springframework.context.BeanThatListens;
import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
+import org.springframework.context.support.StaticMessageSource;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.Order;
@@ -48,6 +53,7 @@ import static org.mockito.BDDMockito.*;
* @author Alef Arendsen
* @author Rick Evans
* @author Stephane Nicoll
+ * @author Juergen Hoeller
*/
public class ApplicationContextEventTests extends AbstractApplicationEventListenerTests {
@@ -337,6 +343,21 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
context.close();
}
+ @Test
+ public void beanPostProcessorPublishesEvents() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ context.registerBeanDefinition("listener", new RootBeanDefinition(BeanThatListens.class));
+ context.registerBeanDefinition("messageSource", new RootBeanDefinition(StaticMessageSource.class));
+ context.registerBeanDefinition("postProcessor", new RootBeanDefinition(EventPublishingBeanPostProcessor.class));
+ context.refresh();
+
+ context.publishEvent(new MyEvent(this));
+ BeanThatListens listener = context.getBean(BeanThatListens.class);
+ assertEquals(4, listener.getEventCount());
+
+ context.close();
+ }
+
@SuppressWarnings("serial")
public static class MyEvent extends ApplicationEvent {
@@ -410,6 +431,7 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
}
}
+
@Order(5)
public static class MyOrderedListener3 implements ApplicationListener {
@@ -422,6 +444,7 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
}
+
@Order(50)
public static class MyOrderedListener4 implements ApplicationListener {
@@ -437,4 +460,25 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
}
}
+
+ public static class EventPublishingBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
+
+ private ApplicationContext applicationContext;
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ this.applicationContext.publishEvent(new MyEvent(this));
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+ }
+
}