From 9281f820f1a2fa1cf860228418db458cb1d09c0d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 2 May 2018 15:21:40 +0200 Subject: [PATCH] Expose FactoryBean's raw object on retrieval during post-processing Issue: SPR-16783 --- .../support/FactoryBeanRegistrySupport.java | 8 + .../DuplicatePostProcessingTests.java | 138 ++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 spring-context/src/test/java/org/springframework/context/annotation/configuration/DuplicatePostProcessingTests.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java index 68c6fd05026..526f2b22633 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java @@ -107,6 +107,11 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg } else { if (shouldPostProcess) { + if (isSingletonCurrentlyInCreation(beanName)) { + // Temporarily return non-post-processed object, not storing it yet.. + return object; + } + beforeSingletonCreation(beanName); try { object = postProcessObjectFromFactoryBean(object, beanName); } @@ -114,6 +119,9 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } + finally { + afterSingletonCreation(beanName); + } } if (containsSingleton(beanName)) { this.factoryBeanObjectCache.put(beanName, object); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/DuplicatePostProcessingTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/DuplicatePostProcessingTests.java new file mode 100644 index 00000000000..d31b609434f --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/DuplicatePostProcessingTests.java @@ -0,0 +1,138 @@ +/* + * Copyright 2002-2018 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.annotation.configuration; + +import org.junit.Test; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; + +/** + * @author Andy Wilkinson + * @author Juergen Hoeller + */ +public class DuplicatePostProcessingTests { + + @Test + public void testWithFactoryBeanAndEventListener() { + new AnnotationConfigApplicationContext(Config.class).getBean(ExampleBean.class); + } + + + + static class Config { + + @Bean + public ExampleFactoryBean exampleFactory() { + return new ExampleFactoryBean(); + } + + @Bean + public static ExampleBeanPostProcessor exampleBeanPostProcessor() { + return new ExampleBeanPostProcessor(); + } + + @Bean + public ExampleApplicationEventListener exampleApplicationEventListener() { + return new ExampleApplicationEventListener(); + } + } + + + static class ExampleFactoryBean implements FactoryBean { + + private final ExampleBean exampleBean = new ExampleBean(); + + @Override + public ExampleBean getObject() throws Exception { + return this.exampleBean; + } + + @Override + public Class getObjectType() { + return ExampleBean.class; + } + + @Override + public boolean isSingleton() { + return true; + } + } + + + static class ExampleBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware { + + private ApplicationContext applicationContext; + + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof ExampleBean) { + this.applicationContext.publishEvent(new ExampleApplicationEvent(this)); + } + return bean; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + } + + + static class ExampleApplicationEvent extends ApplicationEvent { + + public ExampleApplicationEvent(Object source) { + super(source); + } + } + + + static class ExampleApplicationEventListener implements ApplicationListener, BeanFactoryAware { + + private BeanFactory beanFactory; + + @Override + public void onApplicationEvent(ExampleApplicationEvent event) { + this.beanFactory.getBean(ExampleBean.class); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + } + + + static class ExampleBean { + } + +}