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