001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.context.properties; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Method; 021 022import org.springframework.beans.BeansException; 023import org.springframework.beans.factory.InitializingBean; 024import org.springframework.beans.factory.config.BeanPostProcessor; 025import org.springframework.boot.context.properties.bind.Bindable; 026import org.springframework.context.ApplicationContext; 027import org.springframework.context.ApplicationContextAware; 028import org.springframework.core.Ordered; 029import org.springframework.core.PriorityOrdered; 030import org.springframework.core.ResolvableType; 031import org.springframework.core.annotation.AnnotationUtils; 032import org.springframework.core.env.PropertySources; 033import org.springframework.validation.annotation.Validated; 034 035/** 036 * {@link BeanPostProcessor} to bind {@link PropertySources} to beans annotated with 037 * {@link ConfigurationProperties}. 038 * 039 * @author Dave Syer 040 * @author Phillip Webb 041 * @author Christian Dupuis 042 * @author Stephane Nicoll 043 * @author Madhura Bhave 044 */ 045public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, 046 PriorityOrdered, ApplicationContextAware, InitializingBean { 047 048 /** 049 * The bean name that this post-processor is registered with. 050 */ 051 public static final String BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class 052 .getName(); 053 054 /** 055 * The bean name of the configuration properties validator. 056 */ 057 public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator"; 058 059 private ConfigurationBeanFactoryMetadata beanFactoryMetadata; 060 061 private ApplicationContext applicationContext; 062 063 private ConfigurationPropertiesBinder configurationPropertiesBinder; 064 065 @Override 066 public void setApplicationContext(ApplicationContext applicationContext) 067 throws BeansException { 068 this.applicationContext = applicationContext; 069 } 070 071 @Override 072 public void afterPropertiesSet() throws Exception { 073 // We can't use constructor injection of the application context because 074 // it causes eager factory bean initialization 075 this.beanFactoryMetadata = this.applicationContext.getBean( 076 ConfigurationBeanFactoryMetadata.BEAN_NAME, 077 ConfigurationBeanFactoryMetadata.class); 078 this.configurationPropertiesBinder = new ConfigurationPropertiesBinder( 079 this.applicationContext, VALIDATOR_BEAN_NAME); 080 } 081 082 @Override 083 public int getOrder() { 084 return Ordered.HIGHEST_PRECEDENCE + 1; 085 } 086 087 @Override 088 public Object postProcessBeforeInitialization(Object bean, String beanName) 089 throws BeansException { 090 ConfigurationProperties annotation = getAnnotation(bean, beanName, 091 ConfigurationProperties.class); 092 if (annotation != null) { 093 bind(bean, beanName, annotation); 094 } 095 return bean; 096 } 097 098 private void bind(Object bean, String beanName, ConfigurationProperties annotation) { 099 ResolvableType type = getBeanType(bean, beanName); 100 Validated validated = getAnnotation(bean, beanName, Validated.class); 101 Annotation[] annotations = (validated != null) 102 ? new Annotation[] { annotation, validated } 103 : new Annotation[] { annotation }; 104 Bindable<?> target = Bindable.of(type).withExistingValue(bean) 105 .withAnnotations(annotations); 106 try { 107 this.configurationPropertiesBinder.bind(target); 108 } 109 catch (Exception ex) { 110 throw new ConfigurationPropertiesBindException(beanName, bean, annotation, 111 ex); 112 } 113 } 114 115 private ResolvableType getBeanType(Object bean, String beanName) { 116 Method factoryMethod = this.beanFactoryMetadata.findFactoryMethod(beanName); 117 if (factoryMethod != null) { 118 return ResolvableType.forMethodReturnType(factoryMethod); 119 } 120 return ResolvableType.forClass(bean.getClass()); 121 } 122 123 private <A extends Annotation> A getAnnotation(Object bean, String beanName, 124 Class<A> type) { 125 A annotation = this.beanFactoryMetadata.findFactoryAnnotation(beanName, type); 126 if (annotation == null) { 127 annotation = AnnotationUtils.findAnnotation(bean.getClass(), type); 128 } 129 return annotation; 130 } 131 132}