Remove `isImmutable` and `getPrefix` from `OriginLookup`

Drop `isImmutable` and `getPrefix` from `OriginLookup` since
they're not really Origin concerns. Now that `env` is a
foundational layer we can add a dedicated `PropertySourceInfo`
interface and add that to the `o.s.b.env` package without
creating a package tangle.

Closes gh-45547
This commit is contained in:
Phillip Webb 2025-09-16 21:57:03 -07:00
parent 24c25aeae4
commit fc68b001d8
12 changed files with 77 additions and 59 deletions

View File

@ -45,6 +45,9 @@
<subpackage name="ssl"> <subpackage name="ssl">
<disallow pkg="org.springframework.boot" exact-match="true"/> <disallow pkg="org.springframework.boot" exact-match="true"/>
</subpackage> </subpackage>
<subpackage name="origin">
<disallow pkg="org.springframework.boot" exact-match="true"/>
</subpackage>
<subpackage name="util"> <subpackage name="util">
<disallow pkg="org.springframework.boot" exact-match="true"/> <disallow pkg="org.springframework.boot" exact-match="true"/>
</subpackage> </subpackage>

View File

@ -21,8 +21,7 @@ import java.util.Map;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.boot.origin.Origin; import org.springframework.boot.env.PropertySourceInfo;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.system.ApplicationPid; import org.springframework.boot.system.ApplicationPid;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
@ -36,7 +35,7 @@ import org.springframework.util.StringUtils;
* *
* @author Moritz Halbritter * @author Moritz Halbritter
*/ */
class ApplicationInfoPropertySource extends MapPropertySource implements OriginLookup<String> { class ApplicationInfoPropertySource extends MapPropertySource implements PropertySourceInfo {
static final String NAME = "applicationInfo"; static final String NAME = "applicationInfo";
@ -48,11 +47,6 @@ class ApplicationInfoPropertySource extends MapPropertySource implements OriginL
super(NAME, getProperties(applicationVersion)); super(NAME, getProperties(applicationVersion));
} }
@Override
public @Nullable Origin getOrigin(String key) {
return null;
}
@Override @Override
public boolean isImmutable() { public boolean isImmutable() {
return true; return true;

View File

@ -31,7 +31,7 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver; import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.env.PropertySourceInfo;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
@ -454,8 +454,8 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
private static @Nullable ConfigurationPropertySource asConfigurationPropertySource( private static @Nullable ConfigurationPropertySource asConfigurationPropertySource(
PropertySource<?> propertySource) { PropertySource<?> propertySource) {
ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(propertySource); ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(propertySource);
if (configurationPropertySource != null && propertySource instanceof OriginLookup<?> originLookup) { if (configurationPropertySource != null && propertySource instanceof PropertySourceInfo propertySourceInfo) {
configurationPropertySource = configurationPropertySource.withPrefix(originLookup.getPrefix()); configurationPropertySource = configurationPropertySource.withPrefix(propertySourceInfo.getPrefix());
} }
return configurationPropertySource; return configurationPropertySource;
} }

View File

@ -25,7 +25,7 @@ import java.util.function.Function;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.env.PropertySourceInfo;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
@ -69,8 +69,8 @@ class SpringConfigurationPropertySources implements Iterable<ConfigurationProper
return result; return result;
} }
result = SpringConfigurationPropertySource.from(source); result = SpringConfigurationPropertySource.from(source);
if (source instanceof OriginLookup<?> originLookup) { if (source instanceof PropertySourceInfo propertySourceInfo) {
result = result.withPrefix(originLookup.getPrefix()); result = result.withPrefix(propertySourceInfo.getPrefix());
} }
this.cache.put(source, result); this.cache.put(source, result);
return result; return result;

View File

@ -32,8 +32,8 @@ import java.util.stream.Stream;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.boot.env.PropertySourceInfo;
import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.origin.PropertySourceOrigin; import org.springframework.boot.origin.PropertySourceOrigin;
import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
@ -202,8 +202,8 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
boolean isImmutablePropertySource() { boolean isImmutablePropertySource() {
EnumerablePropertySource<?> source = getPropertySource(); EnumerablePropertySource<?> source = getPropertySource();
if (source instanceof OriginLookup<?> originLookup) { if (source instanceof PropertySourceInfo propertySourceInfo) {
return originLookup.isImmutable(); return propertySourceInfo.isImmutable();
} }
if (StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME.equals(source.getName())) { if (StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME.equals(source.getName())) {
return source.getSource() == System.getenv(); return source.getSource() == System.getenv();

View File

@ -76,7 +76,8 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @since 2.4.0 * @since 2.4.0
*/ */
public class ConfigTreePropertySource extends EnumerablePropertySource<Path> implements OriginLookup<String> { public class ConfigTreePropertySource extends EnumerablePropertySource<Path>
implements PropertySourceInfo, OriginLookup<String> {
private static final int MAX_DEPTH = 100; private static final int MAX_DEPTH = 100;

View File

@ -34,7 +34,8 @@ import org.springframework.core.env.MapPropertySource;
* @since 2.0.0 * @since 2.0.0
* @see OriginTrackedValue * @see OriginTrackedValue
*/ */
public final class OriginTrackedMapPropertySource extends MapPropertySource implements OriginLookup<String> { public final class OriginTrackedMapPropertySource extends MapPropertySource
implements PropertySourceInfo, OriginLookup<String> {
private final boolean immutable; private final boolean immutable;

View File

@ -0,0 +1,53 @@
/*
* Copyright 2012-present 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
*
* https://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.boot.env;
import org.jspecify.annotations.Nullable;
import org.springframework.core.env.PropertySource;
/**
* Interface that can be optionally implemented by a {@link PropertySource} to provide
* additional information.
*
* @author Phillip Webb
* @since 4.0.0
*/
public interface PropertySourceInfo {
/**
* Return {@code true} if this lookup is immutable and has contents that will never
* change.
* @return if the lookup is immutable
*/
default boolean isImmutable() {
return false;
}
/**
* Return the implicit prefix that is applied when performing a lookup or {@code null}
* if no prefix is used. Prefixes can be used to disambiguate keys that would
* otherwise clash. For example, if multiple applications are running on the same
* machine a different prefix can be set on each application to ensure that different
* environment variables are used.
* @return the prefix applied by the lookup class or {@code null}.
*/
default @Nullable String getPrefix() {
return null;
}
}

View File

@ -37,29 +37,6 @@ public interface OriginLookup<K> {
*/ */
@Nullable Origin getOrigin(K key); @Nullable Origin getOrigin(K key);
/**
* Return {@code true} if this lookup is immutable and has contents that will never
* change.
* @return if the lookup is immutable
* @since 2.2.0
*/
default boolean isImmutable() {
return false;
}
/**
* Return the implicit prefix that is applied when performing a lookup or {@code null}
* if no prefix is used. Prefixes can be used to disambiguate keys that would
* otherwise clash. For example, if multiple applications are running on the same
* machine a different prefix can be set on each application to ensure that different
* environment variables are used.
* @return the prefix applied by the lookup class or {@code null}.
* @since 2.5.0
*/
default @Nullable String getPrefix() {
return null;
}
/** /**
* Attempt to look up the origin from the given source. If the source is not a * Attempt to look up the origin from the given source. If the source is not a
* {@link OriginLookup} or if an exception occurs during lookup then {@code null} is * {@link OriginLookup} or if an exception occurs during lookup then {@code null} is

View File

@ -22,6 +22,7 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.EnvironmentPostProcessor; import org.springframework.boot.EnvironmentPostProcessor;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.PropertySourceInfo;
import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.origin.SystemEnvironmentOrigin; import org.springframework.boot.origin.SystemEnvironmentOrigin;
@ -81,7 +82,7 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements
* {@link SystemEnvironmentPropertySource} that also tracks {@link Origin}. * {@link SystemEnvironmentPropertySource} that also tracks {@link Origin}.
*/ */
protected static class OriginAwareSystemEnvironmentPropertySource extends SystemEnvironmentPropertySource protected static class OriginAwareSystemEnvironmentPropertySource extends SystemEnvironmentPropertySource
implements OriginLookup<String> { implements PropertySourceInfo, OriginLookup<String> {
private final @Nullable String prefix; private final @Nullable String prefix;

View File

@ -25,8 +25,7 @@ import java.util.function.Function;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.origin.Origin; import org.springframework.boot.env.PropertySourceInfo;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
@ -207,7 +206,7 @@ class ConfigurationPropertySourcesTests {
return total; return total;
} }
static class TestPropertySource extends MapPropertySource implements OriginLookup<String> { static class TestPropertySource extends MapPropertySource implements PropertySourceInfo {
private final boolean immutable; private final boolean immutable;
@ -226,11 +225,6 @@ class ConfigurationPropertySourcesTests {
return map; return map;
} }
@Override
public Origin getOrigin(String key) {
return null;
}
@Override @Override
public boolean isImmutable() { public boolean isImmutable() {
return this.immutable; return this.immutable;

View File

@ -26,8 +26,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.origin.Origin; import org.springframework.boot.env.PropertySourceInfo;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.AbstractApplicationContext;
@ -116,7 +115,7 @@ public final class ManagementContextAutoConfiguration {
* {@link EnumerablePropertySource} providing {@code local.management.port} support. * {@link EnumerablePropertySource} providing {@code local.management.port} support.
*/ */
static class LocalManagementPortPropertySource extends EnumerablePropertySource<Object> static class LocalManagementPortPropertySource extends EnumerablePropertySource<Object>
implements OriginLookup<String> { implements PropertySourceInfo {
private static final Map<String, String> PROPERTY_MAPPINGS = Map.of("local.management.port", private static final Map<String, String> PROPERTY_MAPPINGS = Map.of("local.management.port",
"local.server.port"); "local.server.port");
@ -141,11 +140,6 @@ public final class ManagementContextAutoConfiguration {
return (mapped != null) ? this.environment.getProperty(mapped) : null; return (mapped != null) ? this.environment.getProperty(mapped) : null;
} }
@Override
public @Nullable Origin getOrigin(String key) {
return null;
}
@Override @Override
public boolean isImmutable() { public boolean isImmutable() {
return true; return true;