Allow MergedBeanDefinitionPostProcessor to handle bean creation

This commit registers the MergedBeanDefinitionPostProcessor instances
on the BeanFactory processed for AOT purposes. This allows beans that
are created at build-time to be post-processed for low-level needs such
as initialization and autowiring.

Closes gh-28777
This commit is contained in:
Stephane Nicoll 2022-07-20 11:55:00 +02:00
parent 9573fc96ec
commit ebe3b37298
3 changed files with 58 additions and 1 deletions

View File

@ -364,7 +364,7 @@ final class PostProcessorRegistrationDelegate {
* Register the given BeanPostProcessor beans.
*/
private static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) {
ConfigurableListableBeanFactory beanFactory, List<? extends BeanPostProcessor> postProcessors) {
if (beanFactory instanceof AbstractBeanFactory) {
// Bulk addition is more efficient against our CopyOnWriteArrayList there
@ -439,6 +439,7 @@ final class PostProcessorRegistrationDelegate {
Class<?> beanType = resolveBeanType(bd);
postProcessRootBeanDefinition(postProcessors, beanName, beanType, bd);
}
registerBeanPostProcessors(this.beanFactory, postProcessors);
}
private void postProcessRootBeanDefinition(List<MergedBeanDefinitionPostProcessor> postProcessors,

View File

@ -431,6 +431,24 @@ class AnnotationConfigApplicationContextTests {
"annotationConfigApplicationContextTests.Config", "testBean");
}
@Test
void refreshForAotCanInstantiateBeanWithAutowiredApplicationContext() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(BeanD.class);
context.refreshForAotProcessing();
BeanD bean = context.getBean(BeanD.class);
assertThat(bean.applicationContext).isSameAs(context);
}
@Test
void refreshForAotCanInstantiateBeanWithFieldAutowiredApplicationContext() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(BeanB.class);
context.refreshForAotProcessing();
BeanB bean = context.getBean(BeanB.class);
assertThat(bean.applicationContext).isSameAs(context);
}
@Configuration
static class Config {
@ -506,6 +524,16 @@ class AnnotationConfigApplicationContextTests {
static class BeanC {}
static class BeanD {
private final ApplicationContext applicationContext;
public BeanD(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
static class NonInstantiatedFactoryBean implements FactoryBean<String> {
NonInstantiatedFactoryBean() {

View File

@ -23,10 +23,12 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.OS;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
@ -407,6 +409,32 @@ class GenericApplicationContextTests {
context.close();
}
@Test
void refreshForAotInvokesBeanPostProcessorContractOnMergedBeanDefinitionPostProcessors() {
MergedBeanDefinitionPostProcessor bpp = new MergedBeanDefinitionPostProcessor() {
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
beanDefinition.setAttribute("mbdppCalled", true);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return (beanName.equals("test") ? "42" : bean);
}
};
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("bpp", BeanDefinitionBuilder.rootBeanDefinition(
MergedBeanDefinitionPostProcessor.class, () -> bpp)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
AbstractBeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(String.class)
.addConstructorArgValue("value").getBeanDefinition();
context.registerBeanDefinition("test", bd);
context.refreshForAotProcessing();
assertThat(context.getBeanFactory().getMergedBeanDefinition("test")
.hasAttribute("mbdppCalled")).isTrue();
assertThat(context.getBean("test")).isEqualTo("42");
}
@Test
void refreshForAotFailsOnAnActiveContext() {
GenericApplicationContext context = new GenericApplicationContext();