• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.41
today-context/src/main/java/infra/context/support/PropertySourcesPlaceholderConfigurer.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.context.support;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.io.IOException;
23
import java.util.Properties;
24

25
import infra.beans.BeansException;
26
import infra.beans.factory.BeanInitializationException;
27
import infra.beans.factory.config.ConfigurableBeanFactory;
28
import infra.beans.factory.config.PlaceholderConfigurerSupport;
29
import infra.context.EnvironmentAware;
30
import infra.core.StringValueResolver;
31
import infra.core.conversion.ConversionService;
32
import infra.core.env.ConfigurableEnvironment;
33
import infra.core.env.ConfigurablePropertyResolver;
34
import infra.core.env.Environment;
35
import infra.core.env.PropertiesPropertySource;
36
import infra.core.env.PropertySource;
37
import infra.core.env.PropertySources;
38
import infra.core.env.PropertySourcesPropertyResolver;
39
import infra.lang.Assert;
40

41
/**
42
 * Specialization of {@link PlaceholderConfigurerSupport} that resolves ${...} placeholders
43
 * within bean definition property values and {@code @Value} annotations against the current
44
 * Framework {@link Environment} and its set of {@link PropertySources}.
45
 *
46
 * <p>Any local properties (e.g. those added via {@link #setProperties}, {@link #setLocations}
47
 * et al.) are added as a {@code PropertySource}. Search precedence of local properties is
48
 * based on the value of the {@link #setLocalOverride localOverride} property, which is by
49
 * default {@code false} meaning that local properties are to be searched last, after all
50
 * environment property sources.
51
 *
52
 * <p>See {@link ConfigurableEnvironment} and related javadocs
53
 * for details on manipulating environment property sources.
54
 *
55
 * @author Chris Beams
56
 * @author Juergen Hoeller
57
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
58
 * @see ConfigurableEnvironment
59
 * @see PlaceholderConfigurerSupport
60
 * @since 4.0 2021/12/12 14:39
61
 */
62
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
3✔
63

64
  /**
65
   * {@value} is the name given to the {@link PropertySource} for the set of
66
   * {@linkplain #mergeProperties() merged properties} supplied to this configurer.
67
   */
68
  public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties";
69

70
  /**
71
   * {@value} is the name given to the {@link PropertySource} that wraps the
72
   * {@linkplain #setEnvironment environment} supplied to this configurer.
73
   */
74
  public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties";
75

76
  @Nullable
77
  private PropertySources propertySources;
78

79
  @Nullable
80
  private PropertySources appliedPropertySources;
81

82
  @Nullable
83
  private Environment environment;
84

85
  /**
86
   * Customize the set of {@link PropertySources} to be used by this configurer.
87
   * <p>Setting this property indicates that environment property sources and
88
   * local properties should be ignored.
89
   *
90
   * @see #postProcessBeanFactory
91
   */
92
  public void setPropertySources(PropertySources propertySources) {
93
    this.propertySources = new PropertySources(propertySources);
6✔
94
  }
1✔
95

96
  /**
97
   * {@code PropertySources} from the given {@link Environment}
98
   * will be searched when replacing ${...} placeholders.
99
   *
100
   * @see #setPropertySources
101
   * @see #postProcessBeanFactory
102
   */
103
  @Override
104
  public void setEnvironment(@Nullable Environment environment) {
105
    this.environment = environment;
3✔
106
  }
1✔
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 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(ConfigurableBeanFactory beanFactory) throws BeansException {
125
    if (this.propertySources == null) {
3✔
126
      this.propertySources = new PropertySources();
5✔
127
      if (this.environment != null) {
3✔
128
        PropertySource<?> environmentPropertySource = this.environment instanceof ConfigurableEnvironment ce
9!
129
                ? new ConfigurableEnvironmentPropertySource(ce)
5✔
130
                : new FallbackEnvironmentPropertySource(this.environment);
1✔
131
        this.propertySources.addLast(environmentPropertySource);
4✔
132
      }
133
      try {
134
        PropertySource<?> localPropertySource =
4✔
135
                new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
3✔
136
        if (this.localOverride) {
3✔
137
          this.propertySources.addFirst(localPropertySource);
5✔
138
        }
139
        else {
140
          this.propertySources.addLast(localPropertySource);
4✔
141
        }
142
      }
143
      catch (IOException ex) {
×
144
        throw new BeanInitializationException("Could not load properties", ex);
×
145
      }
1✔
146
    }
147

148
    processProperties(beanFactory, createPropertyResolver(this.propertySources));
7✔
149
    this.appliedPropertySources = this.propertySources;
4✔
150
  }
1✔
151

152
  /**
153
   * Create a {@link ConfigurablePropertyResolver} for the specified property sources.
154
   *
155
   * @param propertySources the property sources to use
156
   */
157
  protected ConfigurablePropertyResolver createPropertyResolver(PropertySources propertySources) {
158
    return new PropertySourcesPropertyResolver(propertySources);
5✔
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(ConfigurableBeanFactory beanFactoryToProcess,
166
          ConfigurablePropertyResolver propertyResolver) throws BeansException {
167

168
    propertyResolver.setValueSeparator(valueSeparator);
4✔
169
    propertyResolver.setPlaceholderPrefix(placeholderPrefix);
4✔
170
    propertyResolver.setPlaceholderSuffix(placeholderSuffix);
4✔
171
    propertyResolver.setEscapeCharacter(escapeCharacter);
4✔
172

173
    StringValueResolver valueResolver = strVal -> {
4✔
174
      String resolved = ignoreUnresolvablePlaceholders ?
3✔
175
              propertyResolver.resolvePlaceholders(strVal) :
4✔
176
              propertyResolver.resolveRequiredPlaceholders(strVal);
4✔
177
      if (this.trimValues) {
3✔
178
        resolved = resolved.trim();
3✔
179
      }
180
      return resolved.equals(nullValue) ? null : resolved;
9✔
181
    };
182

183
    doProcessProperties(beanFactoryToProcess, valueResolver);
4✔
184
  }
1✔
185

186
  /**
187
   * Implemented for compatibility with
188
   * {@link PlaceholderConfigurerSupport}.
189
   *
190
   * @throws UnsupportedOperationException in this implementation
191
   * use {@link #processProperties(ConfigurableBeanFactory, ConfigurablePropertyResolver)}
192
   */
193
  @Override
194
  protected void processProperties(ConfigurableBeanFactory beanFactory, Properties props) {
195
    throw new UnsupportedOperationException(
×
196
            "Call processProperties(ConfigurableBeanFactory, ConfigurablePropertyResolver) instead");
197
  }
198

199
  /**
200
   * Return the property sources that were actually applied during
201
   * {@link #postProcessBeanFactory(ConfigurableBeanFactory) post-processing}.
202
   *
203
   * @return the property sources that were applied
204
   * @throws IllegalStateException if the property sources have not yet been applied
205
   */
206
  public PropertySources getAppliedPropertySources() throws IllegalStateException {
207
    Assert.state(this.appliedPropertySources != null, "PropertySources have not yet been applied");
8✔
208
    return this.appliedPropertySources;
3✔
209
  }
210

211
  /**
212
   * Custom {@link PropertySource} that delegates to the
213
   * {@link ConfigurableEnvironment#getPropertySources() PropertySources} in a
214
   * {@link ConfigurableEnvironment}.
215
   *
216
   * @since 5.0
217
   */
218
  private static class ConfigurableEnvironmentPropertySource extends PropertySource<ConfigurableEnvironment> {
219

220
    ConfigurableEnvironmentPropertySource(ConfigurableEnvironment environment) {
221
      super(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, environment);
4✔
222
    }
1✔
223

224
    @Override
225
    public boolean containsProperty(String name) {
226
      for (PropertySource<?> propertySource : super.source.getPropertySources()) {
×
227
        if (propertySource.containsProperty(name)) {
×
228
          return true;
×
229
        }
230
      }
×
231
      return false;
×
232
    }
233

234
    @Override
235
    @Nullable
236
    // Declare String as covariant return type, since a String is actually required.
237
    public String getProperty(String name) {
238
      for (PropertySource<?> propertySource : super.source.getPropertySources()) {
13✔
239
        Object candidate = propertySource.getProperty(name);
4✔
240
        if (candidate != null) {
2✔
241
          return convertToString(candidate);
4✔
242
        }
243
      }
1✔
244
      return null;
2✔
245
    }
246

247
    /**
248
     * Convert the supplied value to a {@link String} using the {@link ConversionService}
249
     * from the {@link Environment}.
250
     * <p>This is a modified version of
251
     * {@link infra.core.env.AbstractPropertyResolver#convertValueIfNecessary(Object, Class)}.
252
     *
253
     * @param value the value to convert
254
     * @return the converted value, or the original value if no conversion is necessary
255
     */
256
    @Nullable
257
    private String convertToString(Object value) {
258
      if (value instanceof String string) {
6✔
259
        return string;
2✔
260
      }
261
      return super.source.getConversionService().convert(value, String.class);
9✔
262
    }
263

264
    @Override
265
    public String toString() {
266
      return "ConfigurableEnvironmentPropertySource {propertySources=" + super.source.getPropertySources() + "}";
×
267
    }
268
  }
269

270
  /**
271
   * Fallback {@link PropertySource} that delegates to a raw {@link Environment}.
272
   * <p>Should never apply in a regular scenario, since the {@code Environment}
273
   * in an {@code ApplicationContext} should always be a {@link ConfigurableEnvironment}.
274
   *
275
   * @since 5.0
276
   */
277
  private static class FallbackEnvironmentPropertySource extends PropertySource<Environment> {
278

279
    FallbackEnvironmentPropertySource(Environment environment) {
280
      super(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, environment);
×
281
    }
×
282

283
    @Override
284
    public boolean containsProperty(String name) {
285
      return super.source.containsProperty(name);
×
286
    }
287

288
    @Nullable
289
    @Override
290
    // Declare String as covariant return type, since a String is actually required.
291
    public String getProperty(String name) {
292
      return super.source.getProperty(name);
×
293
    }
294

295
    @Override
296
    public String toString() {
297
      return "FallbackEnvironmentPropertySource {environment=" + super.source + "}";
×
298
    }
299
  }
300

301
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc