001/* 002 * Copyright 2002-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 * https://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.context.support; 018 019import java.io.IOException; 020import java.util.Properties; 021 022import org.springframework.beans.BeansException; 023import org.springframework.beans.factory.BeanInitializationException; 024import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 025import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; 026import org.springframework.context.EnvironmentAware; 027import org.springframework.core.env.ConfigurablePropertyResolver; 028import org.springframework.core.env.Environment; 029import org.springframework.core.env.MutablePropertySources; 030import org.springframework.core.env.PropertiesPropertySource; 031import org.springframework.core.env.PropertySource; 032import org.springframework.core.env.PropertySources; 033import org.springframework.core.env.PropertySourcesPropertyResolver; 034import org.springframework.lang.Nullable; 035import org.springframework.util.Assert; 036import org.springframework.util.StringValueResolver; 037 038/** 039 * Specialization of {@link PlaceholderConfigurerSupport} that resolves ${...} placeholders 040 * within bean definition property values and {@code @Value} annotations against the current 041 * Spring {@link Environment} and its set of {@link PropertySources}. 042 * 043 * <p>This class is designed as a general replacement for {@code PropertyPlaceholderConfigurer}. 044 * It is used by default to support the {@code property-placeholder} element in working against 045 * the spring-context-3.1 or higher XSD; whereas, spring-context versions <= 3.0 default to 046 * {@code PropertyPlaceholderConfigurer} to ensure backward compatibility. See the spring-context 047 * XSD documentation for complete details. 048 * 049 * <p>Any local properties (e.g. those added via {@link #setProperties}, {@link #setLocations} 050 * et al.) are added as a {@code PropertySource}. Search precedence of local properties is 051 * based on the value of the {@link #setLocalOverride localOverride} property, which is by 052 * default {@code false} meaning that local properties are to be searched last, after all 053 * environment property sources. 054 * 055 * <p>See {@link org.springframework.core.env.ConfigurableEnvironment} and related javadocs 056 * for details on manipulating environment property sources. 057 * 058 * @author Chris Beams 059 * @author Juergen Hoeller 060 * @since 3.1 061 * @see org.springframework.core.env.ConfigurableEnvironment 062 * @see org.springframework.beans.factory.config.PlaceholderConfigurerSupport 063 * @see org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 064 */ 065public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware { 066 067 /** 068 * {@value} is the name given to the {@link PropertySource} for the set of 069 * {@linkplain #mergeProperties() merged properties} supplied to this configurer. 070 */ 071 public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties"; 072 073 /** 074 * {@value} is the name given to the {@link PropertySource} that wraps the 075 * {@linkplain #setEnvironment environment} supplied to this configurer. 076 */ 077 public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties"; 078 079 080 @Nullable 081 private MutablePropertySources propertySources; 082 083 @Nullable 084 private PropertySources appliedPropertySources; 085 086 @Nullable 087 private Environment environment; 088 089 090 /** 091 * Customize the set of {@link PropertySources} to be used by this configurer. 092 * <p>Setting this property indicates that environment property sources and 093 * local properties should be ignored. 094 * @see #postProcessBeanFactory 095 */ 096 public void setPropertySources(PropertySources propertySources) { 097 this.propertySources = new MutablePropertySources(propertySources); 098 } 099 100 /** 101 * {@code PropertySources} from the given {@link Environment} 102 * will be searched when replacing ${...} placeholders. 103 * @see #setPropertySources 104 * @see #postProcessBeanFactory 105 */ 106 @Override 107 public void setEnvironment(Environment environment) { 108 this.environment = environment; 109 } 110 111 112 /** 113 * Processing occurs by replacing ${...} placeholders in bean definitions by resolving each 114 * against this configurer's set of {@link PropertySources}, which includes: 115 * <ul> 116 * <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources 117 * environment property sources}, if an {@code Environment} {@linkplain #setEnvironment is present} 118 * <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any} 119 * {@linkplain #setLocations have} {@linkplain #setProperties been} 120 * {@linkplain #setPropertiesArray specified} 121 * <li>any property sources set by calling {@link #setPropertySources} 122 * </ul> 123 * <p>If {@link #setPropertySources} is called, <strong>environment and local properties will be 124 * ignored</strong>. This method is designed to give the user fine-grained control over property 125 * sources, and once set, the configurer makes no assumptions about adding additional sources. 126 */ 127 @Override 128 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 129 if (this.propertySources == null) { 130 this.propertySources = new MutablePropertySources(); 131 if (this.environment != null) { 132 this.propertySources.addLast( 133 new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) { 134 @Override 135 @Nullable 136 public String getProperty(String key) { 137 return this.source.getProperty(key); 138 } 139 } 140 ); 141 } 142 try { 143 PropertySource<?> localPropertySource = 144 new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties()); 145 if (this.localOverride) { 146 this.propertySources.addFirst(localPropertySource); 147 } 148 else { 149 this.propertySources.addLast(localPropertySource); 150 } 151 } 152 catch (IOException ex) { 153 throw new BeanInitializationException("Could not load properties", ex); 154 } 155 } 156 157 processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources)); 158 this.appliedPropertySources = this.propertySources; 159 } 160 161 /** 162 * Visit each bean definition in the given bean factory and attempt to replace ${...} property 163 * placeholders with values from the given properties. 164 */ 165 protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, 166 final ConfigurablePropertyResolver propertyResolver) throws BeansException { 167 168 propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); 169 propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); 170 propertyResolver.setValueSeparator(this.valueSeparator); 171 172 StringValueResolver valueResolver = strVal -> { 173 String resolved = (this.ignoreUnresolvablePlaceholders ? 174 propertyResolver.resolvePlaceholders(strVal) : 175 propertyResolver.resolveRequiredPlaceholders(strVal)); 176 if (this.trimValues) { 177 resolved = resolved.trim(); 178 } 179 return (resolved.equals(this.nullValue) ? null : resolved); 180 }; 181 182 doProcessProperties(beanFactoryToProcess, valueResolver); 183 } 184 185 /** 186 * Implemented for compatibility with 187 * {@link org.springframework.beans.factory.config.PlaceholderConfigurerSupport}. 188 * @deprecated in favor of 189 * {@link #processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver)} 190 * @throws UnsupportedOperationException in this implementation 191 */ 192 @Override 193 @Deprecated 194 protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) { 195 throw new UnsupportedOperationException( 196 "Call processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver) instead"); 197 } 198 199 /** 200 * Return the property sources that were actually applied during 201 * {@link #postProcessBeanFactory(ConfigurableListableBeanFactory) post-processing}. 202 * @return the property sources that were applied 203 * @throws IllegalStateException if the property sources have not yet been applied 204 * @since 4.0 205 */ 206 public PropertySources getAppliedPropertySources() throws IllegalStateException { 207 Assert.state(this.appliedPropertySources != null, "PropertySources have not yet been applied"); 208 return this.appliedPropertySources; 209 } 210 211}