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