From aa49949d7ad09944db4284b957d754e8189ecc74 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 16 Apr 2015 18:15:33 +0200 Subject: [PATCH 1/3] Bean type mismatch check accepts assignable values according to ClassUtils Issue: SPR-12905 --- .../context/annotation/ConfigurationClassEnhancer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index e22edafb9b0..fa9eb47055d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -46,6 +46,7 @@ import org.springframework.cglib.transform.ClassEmitterTransformer; import org.springframework.cglib.transform.TransformingClassGenerator; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -322,7 +323,7 @@ class ConfigurationClassEnhancer { } Object beanInstance = (!ObjectUtils.isEmpty(beanMethodArgs) ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); - if (beanInstance != null && !beanMethod.getReturnType().isInstance(beanInstance)) { + if (beanInstance != null && !ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) { String msg = String.format("@Bean method %s.%s called as a bean reference " + "for type [%s] but overridden by non-compatible bean instance of type [%s].", beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(), From 65ba72f1fc61c5424564cc94267d9a0733645851 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 16 Apr 2015 18:16:03 +0200 Subject: [PATCH 2/3] FactoryBean type check logs currently-in-creation exception at debug level Issue: SPR-12900 --- .../beans/factory/support/AbstractBeanFactory.java | 12 +++++++++--- .../beans/factory/FactoryBeanTests.java | 8 +++++++- .../factory/FactoryBeanTests-withAutowiring.xml | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 00eab29578f..8853e4daf8f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1451,9 +1451,15 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp return getTypeForFactoryBean(factoryBean); } catch (BeanCreationException ex) { - // Can only happen when getting a FactoryBean. - if (logger.isWarnEnabled()) { - logger.warn("Bean creation exception on FactoryBean type check: " + ex); + if (ex instanceof BeanCurrentlyInCreationException) { + if (logger.isDebugEnabled()) { + logger.debug("Bean currently in creation on FactoryBean type check: " + ex); + } + } + else { + if (logger.isWarnEnabled()) { + logger.warn("Bean creation exception on FactoryBean type check: " + ex); + } } onSuppressedException(ex); return null; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java index 14a4eb63d01..c731260f652 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java @@ -38,7 +38,7 @@ import static org.springframework.tests.TestResourceUtils.*; * @author Juergen Hoeller * @author Chris Beams */ -public final class FactoryBeanTests { +public class FactoryBeanTests { private static final Class CLASS = FactoryBeanTests.class; private static final Resource RETURNS_NULL_CONTEXT = qualifiedResource(CLASS, "returnsNull.xml"); @@ -63,10 +63,13 @@ public final class FactoryBeanTests { BeanFactoryPostProcessor ppc = (BeanFactoryPostProcessor) factory.getBean("propertyPlaceholderConfigurer"); ppc.postProcessBeanFactory(factory); + assertNull(factory.getType("betaFactory")); + Alpha alpha = (Alpha) factory.getBean("alpha"); Beta beta = (Beta) factory.getBean("beta"); Gamma gamma = (Gamma) factory.getBean("gamma"); Gamma gamma2 = (Gamma) factory.getBean("gammaFactory"); + assertSame(beta, alpha.getBeta()); assertSame(gamma, beta.getGamma()); assertSame(gamma2, beta.getGamma()); @@ -194,6 +197,9 @@ public final class FactoryBeanTests { @Component public static class BetaFactoryBean implements FactoryBean { + public BetaFactoryBean(Alpha alpha) { + } + private Beta beta; public void setBeta(Beta beta) { diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/FactoryBeanTests-withAutowiring.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/FactoryBeanTests-withAutowiring.xml index 13f328c373a..0f166640440 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/FactoryBeanTests-withAutowiring.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/FactoryBeanTests-withAutowiring.xml @@ -11,7 +11,7 @@ - + From 9ed0a56d840886e4d6d9d90a4e21066c789f128e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 16 Apr 2015 18:16:15 +0200 Subject: [PATCH 3/3] AbstractApplicationContext collects early ApplicationEvents and publishes them once the multicaster is available Issue: SPR-12902 --- .../context/ApplicationEventPublisher.java | 7 +-- .../support/AbstractApplicationContext.java | 44 +++++++++++++++---- .../event/ApplicationContextEventTests.java | 44 +++++++++++++++++++ 3 files changed, 84 insertions(+), 11 deletions(-) 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; + } + } + }