Merge pull request #22858 from dsyer

* pr/22858:
  Polish 'Support ConfigurationClassPostProcessor supplier'
  Support ConfigurationClassPostProcessor supplier

Closes gh-22858
pull/22898/head
Phillip Webb 4 years ago
commit 4f8bf4d7dd

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,18 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import java.util.function.Supplier;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
@ -44,6 +49,7 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
* {@link ConfigurationClassPostProcessor} and Spring Boot. * {@link ConfigurationClassPostProcessor} and Spring Boot.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer
*/ */
class SharedMetadataReaderFactoryContextInitializer class SharedMetadataReaderFactoryContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered { implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
@ -53,7 +59,8 @@ class SharedMetadataReaderFactoryContextInitializer
@Override @Override
public void initialize(ConfigurableApplicationContext applicationContext) { public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor()); BeanFactoryPostProcessor postProcessor = new CachingMetadataReaderFactoryPostProcessor(applicationContext);
applicationContext.addBeanFactoryPostProcessor(postProcessor);
} }
@Override @Override
@ -66,9 +73,15 @@ class SharedMetadataReaderFactoryContextInitializer
* {@link CachingMetadataReaderFactory} and configure the * {@link CachingMetadataReaderFactory} and configure the
* {@link ConfigurationClassPostProcessor}. * {@link ConfigurationClassPostProcessor}.
*/ */
private static class CachingMetadataReaderFactoryPostProcessor static class CachingMetadataReaderFactoryPostProcessor
implements BeanDefinitionRegistryPostProcessor, PriorityOrdered { implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
private ConfigurableApplicationContext context;
CachingMetadataReaderFactoryPostProcessor(ConfigurableApplicationContext context) {
this.context = context;
}
@Override @Override
public int getOrder() { public int getOrder() {
// Must happen before the ConfigurationClassPostProcessor is created // Must happen before the ConfigurationClassPostProcessor is created
@ -94,14 +107,66 @@ class SharedMetadataReaderFactoryContextInitializer
private void configureConfigurationClassPostProcessor(BeanDefinitionRegistry registry) { private void configureConfigurationClassPostProcessor(BeanDefinitionRegistry registry) {
try { try {
BeanDefinition definition = registry configureConfigurationClassPostProcessor(
.getBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME); registry.getBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
definition.getPropertyValues().add("metadataReaderFactory", new RuntimeBeanReference(BEAN_NAME));
} }
catch (NoSuchBeanDefinitionException ex) { catch (NoSuchBeanDefinitionException ex) {
} }
} }
private void configureConfigurationClassPostProcessor(BeanDefinition definition) {
if (definition instanceof AbstractBeanDefinition) {
configureConfigurationClassPostProcessor((AbstractBeanDefinition) definition);
return;
}
configureConfigurationClassPostProcessor(definition.getPropertyValues());
}
private void configureConfigurationClassPostProcessor(AbstractBeanDefinition definition) {
Supplier<?> instanceSupplier = definition.getInstanceSupplier();
if (instanceSupplier != null) {
definition.setInstanceSupplier(
new ConfigurationClassPostProcessorCustomizingSupplier(this.context, instanceSupplier));
return;
}
configureConfigurationClassPostProcessor(definition.getPropertyValues());
}
private void configureConfigurationClassPostProcessor(MutablePropertyValues propertyValues) {
propertyValues.add("metadataReaderFactory", new RuntimeBeanReference(BEAN_NAME));
}
}
/**
* {@link Supplier} used to customize the {@link ConfigurationClassPostProcessor} when
* it's first created.
*/
static class ConfigurationClassPostProcessorCustomizingSupplier implements Supplier<Object> {
private final ConfigurableApplicationContext context;
private final Supplier<?> instanceSupplier;
ConfigurationClassPostProcessorCustomizingSupplier(ConfigurableApplicationContext context,
Supplier<?> instanceSupplier) {
this.context = context;
this.instanceSupplier = instanceSupplier;
}
@Override
public Object get() {
Object instance = this.instanceSupplier.get();
if (instance instanceof ConfigurationClassPostProcessor) {
configureConfigurationClassPostProcessor((ConfigurationClassPostProcessor) instance);
}
return instance;
}
private void configureConfigurationClassPostProcessor(ConfigurationClassPostProcessor instance) {
instance.setMetadataReaderFactory(this.context.getBean(BEAN_NAME, MetadataReaderFactory.class));
}
} }
/** /**

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,32 +19,43 @@ package org.springframework.boot.autoconfigure;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType; import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor;
import org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link SharedMetadataReaderFactoryContextInitializer}. * Tests for {@link SharedMetadataReaderFactoryContextInitializer}.
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb
*/ */
class SharedMetadataReaderFactoryContextInitializerTests { class SharedMetadataReaderFactoryContextInitializerTests {
@Test @Test
@SuppressWarnings("unchecked")
void checkOrderOfInitializer() { void checkOrderOfInitializer() {
SpringApplication application = new SpringApplication(TestConfig.class); SpringApplication application = new SpringApplication(TestConfig.class);
application.setWebApplicationType(WebApplicationType.NONE); application.setWebApplicationType(WebApplicationType.NONE);
@SuppressWarnings("unchecked")
List<ApplicationContextInitializer<?>> initializers = (List<ApplicationContextInitializer<?>>) ReflectionTestUtils List<ApplicationContextInitializer<?>> initializers = (List<ApplicationContextInitializer<?>>) ReflectionTestUtils
.getField(application, "initializers"); .getField(application, "initializers");
// Simulate what would happen if an initializer was added using spring.factories // Simulate what would happen if an initializer was added using spring.factories
@ -55,6 +66,30 @@ class SharedMetadataReaderFactoryContextInitializerTests {
assertThat(definition.getAttribute("seen")).isEqualTo(true); assertThat(definition.getAttribute("seen")).isEqualTo(true);
} }
@Test
void initializeWhenUsingSupplierDecorates() {
GenericApplicationContext context = new GenericApplicationContext();
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
ConfigurationClassPostProcessor configurationAnnotationPostProcessor = mock(
ConfigurationClassPostProcessor.class);
BeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(ConfigurationClassPostProcessor.class).getBeanDefinition();
((AbstractBeanDefinition) beanDefinition).setInstanceSupplier(() -> configurationAnnotationPostProcessor);
registry.registerBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME,
beanDefinition);
CachingMetadataReaderFactoryPostProcessor postProcessor = new CachingMetadataReaderFactoryPostProcessor(
context);
postProcessor.postProcessBeanDefinitionRegistry(registry);
context.refresh();
ConfigurationClassPostProcessor bean = context.getBean(ConfigurationClassPostProcessor.class);
assertThat(bean).isSameAs(configurationAnnotationPostProcessor);
ArgumentCaptor<MetadataReaderFactory> metadataReaderFactory = ArgumentCaptor
.forClass(MetadataReaderFactory.class);
verify(configurationAnnotationPostProcessor).setMetadataReaderFactory(metadataReaderFactory.capture());
assertThat(metadataReaderFactory.getValue())
.isInstanceOf(ConcurrentReferenceCachingMetadataReaderFactory.class);
}
static class TestConfig { static class TestConfig {
} }

Loading…
Cancel
Save