Use Registrar to register BeanPostProcessors

Use a ImportBeanDefinitionRegistrar to register BeanPostProcessors
rather than using @Bean definitions. Prevents warnings about
beans not having all BeanPostProcessors applied.
This commit is contained in:
Phillip Webb
2013-07-24 16:58:50 -07:00
parent 132722100a
commit e695b4a6ea
5 changed files with 243 additions and 169 deletions

View File

@@ -22,10 +22,17 @@ import org.apache.catalina.startup.Tomcat;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Loader;
import org.springframework.autoconfigure.EnableAutoConfiguration;
import org.springframework.autoconfigure.web.EmbeddedServletContainerAutoConfiguration.EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.bootstrap.context.condition.ConditionalOnClass;
import org.springframework.bootstrap.context.condition.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.condition.SearchStrategy;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.ServletContextInitializer;
@@ -33,8 +40,11 @@ import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServlet
import org.springframework.bootstrap.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.web.servlet.DispatcherServlet;
/**
@@ -44,18 +54,10 @@ import org.springframework.web.servlet.DispatcherServlet;
* @author Dave Syer
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@Import(EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
/**
* Support {@link EmbeddedServletContainerCustomizerBeanPostProcessor} to apply
* {@link EmbeddedServletContainerCustomizer}s.
*/
@Bean
@ConditionalOnMissingBean(value = EmbeddedServletContainerCustomizerBeanPostProcessor.class, search = SearchStrategy.CURRENT)
public EmbeddedServletContainerCustomizerBeanPostProcessor embeddedServletContainerCustomizerBeanPostProcessor() {
return new EmbeddedServletContainerCustomizerBeanPostProcessor();
}
/**
* Add the {@link DispatcherServlet} unless the user has defined their own
* {@link ServletContextInitializer}s.
@@ -101,4 +103,35 @@ public class EmbeddedServletContainerAutoConfiguration {
}
/**
* Registers a {@link EmbeddedServletContainerCustomizerBeanPostProcessor}. Registered
* via {@link ImportBeanDefinitionRegistrar} for early registration.
*/
public static class EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar
implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory != null
&& this.beanFactory.getBeansOfType(
EmbeddedServletContainerCustomizerBeanPostProcessor.class)
.size() == 0) {
BeanDefinition beanDefinition = new RootBeanDefinition(
EmbeddedServletContainerCustomizerBeanPostProcessor.class);
registry.registerBeanDefinition(
"embeddedServletContainerCustomizerBeanPostProcessor",
beanDefinition);
}
}
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2012-2013 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.bootstrap.context.properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.bootstrap.context.condition.ConditionalOnClass;
import org.springframework.bootstrap.context.condition.ConditionalOnMissingBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
/**
* Configuration for binding externalized application properties to
* {@link ConfigurationProperties} beans.
*
* @author Dave Syer
*/
@Configuration
public class ConfigurationPropertiesBindingConfiguration {
public final static String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
@Autowired(required = false)
private PropertySourcesPlaceholderConfigurer configurer;
@Autowired(required = false)
private Environment environment;
@Autowired(required = false)
@Qualifier(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME)
private ConversionService conversionService;
@Autowired(required = false)
@Qualifier(VALIDATOR_BEAN_NAME)
private Validator validator;
@ConditionalOnMissingBean(name = VALIDATOR_BEAN_NAME)
@ConditionalOnClass(name = "javax.validation.Validator")
protected static class ValidatorConfiguration {
@Bean
protected Validator configurationPropertiesValidator() {
return new LocalValidatorFactoryBean();
}
}
/**
* Lifecycle hook that binds application properties to any bean whose type is
* decorated with {@link ConfigurationProperties} annotation.
*
* @return a bean post processor to bind application properties
*/
@Bean
public ConfigurationPropertiesBindingPostProcessor propertySourcesBinder() {
PropertySources propertySources;
if (this.configurer != null) {
propertySources = extractPropertySources(this.configurer);
}
else if (this.environment instanceof ConfigurableEnvironment) {
propertySources = flattenPropertySources(((ConfigurableEnvironment) this.environment)
.getPropertySources());
}
else {
// empty, so not very useful, but fulfils the contract
propertySources = new MutablePropertySources();
}
ConfigurationPropertiesBindingPostProcessor processor = new ConfigurationPropertiesBindingPostProcessor();
processor.setValidator(this.validator);
processor.setConversionService(this.conversionService);
processor.setPropertySources(propertySources);
return processor;
}
/**
* Flatten out a tree of property sources.
*
* @param propertySources some PropertySources, possibly containing environment
* properties
* @return another PropertySources containing the same properties
*/
private PropertySources flattenPropertySources(PropertySources propertySources) {
MutablePropertySources result = new MutablePropertySources();
for (PropertySource<?> propertySource : propertySources) {
flattenPropertySources(propertySource, result);
}
return result;
}
/**
* Convenience method to allow recursive flattening of property sources.
*
* @param propertySource a property source to flatten
* @param result the cumulative result
*/
private void flattenPropertySources(PropertySource<?> propertySource,
MutablePropertySources result) {
Object source = propertySource.getSource();
if (source instanceof ConfigurableEnvironment) {
ConfigurableEnvironment environment = (ConfigurableEnvironment) source;
for (PropertySource<?> childSource : environment.getPropertySources()) {
flattenPropertySources(childSource, result);
}
}
else {
result.addLast(propertySource);
}
}
/**
* Convenience method to extract PropertySources from an existing (and already
* initialized) PropertySourcesPlaceholderConfigurer. As long as this method is
* executed late enough in the context lifecycle it will come back with data. We can
* rely on the fact that PropertySourcesPlaceholderConfigurer is a
* BeanFactoryPostProcessor and is therefore initialized early.
*
* @param configurer a PropertySourcesPlaceholderConfigurer
* @return some PropertySources
*/
private PropertySources extractPropertySources(
PropertySourcesPlaceholderConfigurer configurer) {
PropertySources propertySources = configurer.getAppliedPropertySources();
// Flatten the sources into a single list so they can be iterated
return flattenPropertySources(propertySources);
}
}

View File

@@ -17,21 +17,30 @@
package org.springframework.bootstrap.context.properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.bind.PropertiesConfigurationFactory;
import org.springframework.bootstrap.config.PropertiesPropertySourceLoader;
import org.springframework.bootstrap.config.PropertySourceLoader;
import org.springframework.bootstrap.config.YamlPropertySourceLoader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
@@ -40,22 +49,32 @@ import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
/**
* {@link BeanPostProcessor} to bind {@link PropertySources} to beans annotated with
* {@link ConfigurationProperties}.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {
BeanFactoryAware, ResourceLoaderAware, EnvironmentAware, BeanClassLoaderAware,
ApplicationContextAware, InitializingBean, DisposableBean {
public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
private static final String VALIDATOR_CLASS = "javax.validation.Validator";
private PropertySources propertySources;
private Validator validator;
private boolean ownedValidator = false;
private ConversionService conversionService;
private DefaultConversionService defaultConversionService = new DefaultConversionService();
@@ -68,6 +87,10 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
private Environment environment = new StandardEnvironment();
private ClassLoader beanClassLoader;
private ApplicationContext applicationContext;
/**
* @param propertySources
*/
@@ -104,6 +127,126 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
this.environment = environment;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.propertySources == null) {
this.propertySources = deducePropertySources();
}
if (this.validator == null) {
this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class);
if (this.validator == null
&& ClassUtils.isPresent(VALIDATOR_CLASS, this.beanClassLoader)) {
LocalValidatorFactoryBean validatorToUse = new LocalValidatorFactoryBean();
validatorToUse.setApplicationContext(this.applicationContext);
validatorToUse.afterPropertiesSet();
this.validator = validatorToUse;
this.ownedValidator = true;
}
}
if (this.conversionService == null) {
this.conversionService = getOptionalBean(
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
ConversionService.class);
}
}
@Override
public void destroy() throws Exception {
if (this.ownedValidator) {
((DisposableBean) this.validator).destroy();
}
}
private PropertySources deducePropertySources() {
try {
PropertySourcesPlaceholderConfigurer configurer = this.beanFactory
.getBean(PropertySourcesPlaceholderConfigurer.class);
return extractPropertySources(configurer);
}
catch (NoSuchBeanDefinitionException ex) {
// Continue if no PropertySourcesPlaceholderConfigurer bean
}
if (this.environment instanceof ConfigurableEnvironment) {
return flattenPropertySources(((ConfigurableEnvironment) this.environment)
.getPropertySources());
}
// empty, so not very useful, but fulfils the contract
return new MutablePropertySources();
}
private <T> T getOptionalBean(String name, Class<T> type) {
try {
return this.beanFactory.getBean(name, type);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
/**
* Convenience method to extract PropertySources from an existing (and already
* initialized) PropertySourcesPlaceholderConfigurer. As long as this method is
* executed late enough in the context lifecycle it will come back with data. We can
* rely on the fact that PropertySourcesPlaceholderConfigurer is a
* BeanFactoryPostProcessor and is therefore initialized early.
* @param configurer a PropertySourcesPlaceholderConfigurer
* @return some PropertySources
*/
private PropertySources extractPropertySources(
PropertySourcesPlaceholderConfigurer configurer) {
PropertySources propertySources = configurer.getAppliedPropertySources();
// Flatten the sources into a single list so they can be iterated
return flattenPropertySources(propertySources);
}
/**
* Flatten out a tree of property sources.
* @param propertySources some PropertySources, possibly containing environment
* properties
* @return another PropertySources containing the same properties
*/
private PropertySources flattenPropertySources(PropertySources propertySources) {
MutablePropertySources result = new MutablePropertySources();
for (PropertySource<?> propertySource : propertySources) {
flattenPropertySources(propertySource, result);
}
return result;
}
/**
* Convenience method to allow recursive flattening of property sources.
* @param propertySource a property source to flatten
* @param result the cumulative result
*/
private void flattenPropertySources(PropertySource<?> propertySource,
MutablePropertySources result) {
Object source = propertySource.getSource();
if (source instanceof ConfigurableEnvironment) {
ConfigurableEnvironment environment = (ConfigurableEnvironment) source;
for (PropertySource<?> childSource : environment.getPropertySources()) {
flattenPropertySources(childSource, result);
}
}
else {
result.addLast(propertySource);
}
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
@@ -130,7 +273,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
if (annotation != null && annotation.path().length != 0) {
factory.setPropertySources(loadPropertySources(annotation.path()));
} else {
}
else {
factory.setPropertySources(this.propertySources);
}
factory.setValidator(this.validator);
@@ -149,15 +293,18 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
}
try {
factory.bindPropertiesToTarget();
} catch (Exception ex) {
}
catch (Exception ex) {
throw new BeanCreationException(beanName, "Could not bind properties", ex);
}
}
private PropertySources loadPropertySources(String[] path) {
MutablePropertySources propertySources = new MutablePropertySources();
PropertySourceLoader[] loaders = { new PropertiesPropertySourceLoader(),
YamlPropertySourceLoader.springProfileAwareLoader(environment.getActiveProfiles()) };
PropertySourceLoader[] loaders = {
new PropertiesPropertySourceLoader(),
YamlPropertySourceLoader.springProfileAwareLoader(this.environment
.getActiveProfiles()) };
for (String location : path) {
location = this.environment.resolvePlaceholders(location);
Resource resource = this.resourceLoader.getResource(location);

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012-2013 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.bootstrap.context.properties;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* {@link ImportBeanDefinitionRegistrar} for binding externalized application properties
* to {@link ConfigurationProperties} beans.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class ConfigurationPropertiesBindingPostProcessorRegistrar implements
ImportBeanDefinitionRegistrar {
public static final String BINDER_BEAN_NAME = "propertySourcesBinder";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new RootBeanDefinition(
ConfigurationPropertiesBindingPostProcessor.class);
registry.registerBeanDefinition(BINDER_BEAN_NAME, beanDefinition);
}
}

View File

@@ -48,11 +48,11 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
EnableConfigurationProperties.class.getName(), false);
Object[] type = (Object[]) attributes.getFirst("value");
if (type == null || type.length == 0) {
return new String[] { ConfigurationPropertiesBindingConfiguration.class
return new String[] { ConfigurationPropertiesBindingPostProcessorRegistrar.class
.getName() };
}
return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(),
ConfigurationPropertiesBindingConfiguration.class.getName() };
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
}
public static class ConfigurationPropertiesBeanRegistrar implements