inner beans detected as ApplicationListeners as well (SPR-6049)
This commit is contained in:
parent
94aad0b537
commit
0e1b04d082
|
|
@ -99,8 +99,8 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
|
|||
* Add a new ApplicationListener that will be notified on context events
|
||||
* such as context refresh and context shutdown.
|
||||
* <p>Note that any ApplicationListener registered here will be applied
|
||||
* on refresh of this context. If a listener is added after the initial
|
||||
* refresh, it will be applied on next refresh of the context.
|
||||
* on refresh if the context is not active yet, or on the fly with the
|
||||
* current event multicaster in case of a context that is already active.
|
||||
* @param listener the ApplicationListener to register
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
* @see org.springframework.context.event.ContextClosedEvent
|
||||
|
|
|
|||
|
|
@ -20,11 +20,14 @@ import java.io.IOException;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.security.AccessControlException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -193,7 +196,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
private ApplicationEventMulticaster applicationEventMulticaster;
|
||||
|
||||
/** Statically specified listeners */
|
||||
private List<ApplicationListener> applicationListeners = new ArrayList<ApplicationListener>();
|
||||
private Set<ApplicationListener> applicationListeners = new LinkedHashSet<ApplicationListener>();
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -362,13 +365,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
}
|
||||
|
||||
public void addApplicationListener(ApplicationListener listener) {
|
||||
this.applicationListeners.add(listener);
|
||||
if (isActive()) {
|
||||
addListener(listener);
|
||||
}
|
||||
else {
|
||||
this.applicationListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of statically specified ApplicationListeners.
|
||||
*/
|
||||
public List<ApplicationListener> getApplicationListeners() {
|
||||
public Collection<ApplicationListener> getApplicationListeners() {
|
||||
return this.applicationListeners;
|
||||
}
|
||||
|
||||
|
|
@ -817,7 +825,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
// 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 (final String lisName : listenerBeanNames) {
|
||||
for (String lisName : listenerBeanNames) {
|
||||
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,18 @@ package org.springframework.context.support;
|
|||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
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.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
|
@ -48,10 +55,14 @@ import org.springframework.context.ResourceLoaderAware;
|
|||
* @see org.springframework.context.ApplicationContextAware
|
||||
* @see org.springframework.context.support.AbstractApplicationContext#refresh()
|
||||
*/
|
||||
class ApplicationContextAwareProcessor implements BeanPostProcessor {
|
||||
class ApplicationContextAwareProcessor implements MergedBeanDefinitionPostProcessor {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final ConfigurableApplicationContext applicationContext;
|
||||
|
||||
private final Map<String, Boolean> singletonNames = new ConcurrentHashMap<String, Boolean>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ApplicationContextAwareProcessor for the given context.
|
||||
|
|
@ -61,7 +72,13 @@ class ApplicationContextAwareProcessor implements BeanPostProcessor {
|
|||
}
|
||||
|
||||
|
||||
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
|
||||
if (!this.applicationContext.containsBean(beanName) && beanDefinition.isSingleton()) {
|
||||
this.singletonNames.put(beanName, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
|
||||
AccessControlContext acc = null;
|
||||
|
||||
if (System.getSecurityManager() != null &&
|
||||
|
|
@ -73,19 +90,19 @@ class ApplicationContextAwareProcessor implements BeanPostProcessor {
|
|||
if (acc != null) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||
public Object run() {
|
||||
doProcess(bean);
|
||||
doProcess(bean, beanName);
|
||||
return null;
|
||||
}
|
||||
}, acc);
|
||||
}
|
||||
else {
|
||||
doProcess(bean);
|
||||
doProcess(bean, beanName);
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void doProcess(Object bean) {
|
||||
private void doProcess(Object bean, String beanName) {
|
||||
if (bean instanceof ResourceLoaderAware) {
|
||||
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
|
||||
}
|
||||
|
|
@ -98,6 +115,27 @@ class ApplicationContextAwareProcessor implements BeanPostProcessor {
|
|||
if (bean instanceof ApplicationContextAware) {
|
||||
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
|
||||
}
|
||||
|
||||
if (bean instanceof ApplicationListener) {
|
||||
if (!this.applicationContext.containsBean(beanName)) {
|
||||
// not a top-level bean - not detected as a listener by getBeanNamesForType retrieval
|
||||
Boolean flag = this.singletonNames.get(beanName);
|
||||
if (Boolean.TRUE.equals(flag)) {
|
||||
// inner singleton bean: register on the fly
|
||||
this.applicationContext.addApplicationListener((ApplicationListener) bean);
|
||||
}
|
||||
else if (flag == null) {
|
||||
// inner bean with other scope - can't reliably process events
|
||||
if (logger.isWarnEnabled()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String name) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import static org.easymock.EasyMock.*;
|
|||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
@ -126,6 +127,19 @@ public class ApplicationContextEventTests {
|
|||
assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void innerBeanAsListener() {
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
RootBeanDefinition listenerDef = new RootBeanDefinition(TestBean.class);
|
||||
listenerDef.getPropertyValues().addPropertyValue("friends", new RootBeanDefinition(BeanThatListens.class));
|
||||
context.registerBeanDefinition("listener", listenerDef);
|
||||
context.refresh();
|
||||
context.publishEvent(new MyEvent(this));
|
||||
context.publishEvent(new MyEvent(this));
|
||||
TestBean listener = context.getBean(TestBean.class);
|
||||
assertEquals(3, ((BeanThatListens) listener.getFriends().iterator().next()).getEventCount());
|
||||
}
|
||||
|
||||
|
||||
public static class MyEvent extends ApplicationEvent {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue