diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesTests.java index 52e9f9be3b..f7e05e2630 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -19,9 +19,13 @@ package org.springframework.boot.context.properties.source; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginLookup; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; @@ -121,4 +125,71 @@ class ConfigurationPropertySourcesTests { assertThat(configurationSources.iterator()).toIterable().hasSize(5); } + @Test // gh-20625 + void environmentPropertyAccessWhenImmutableShouldBePerformant() { + testPropertySourcePerformance(true, 1000); + } + + @Test // gh-20625 + @Disabled("for manual testing") + void environmentPropertyAccessWhenMutableShouldBeTolerable() { + testPropertySourcePerformance(false, 5000); + } + + private void testPropertySourcePerformance(boolean immutable, int maxTime) { + StandardEnvironment environment = createPerformanceTestEnvironment(immutable); + testPropertySourcePerformance(environment, maxTime); + } + + private StandardEnvironment createPerformanceTestEnvironment(boolean immutable) { + StandardEnvironment environment = new StandardEnvironment(); + MutablePropertySources propertySources = environment.getPropertySources(); + for (int i = 0; i < 100; i++) { + propertySources.addLast(new TestPropertySource(i, immutable)); + } + ConfigurationPropertySources.attach(environment); + return environment; + } + + private void testPropertySourcePerformance(StandardEnvironment environment, int maxTime) { + long start = System.nanoTime(); + for (int i = 0; i < 1000; i++) { + environment.getProperty("missing" + i); + } + long total = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + assertThat(environment.getProperty("test-10-property-80")).isEqualTo("test-10-property-80-value"); + assertThat(total).isLessThan(maxTime); + } + + static class TestPropertySource extends MapPropertySource implements OriginLookup { + + private final boolean immutable; + + TestPropertySource(int index, boolean immutable) { + super("test-" + index, createProperties(index)); + this.immutable = immutable; + } + + private static Map createProperties(int index) { + Map map = new LinkedHashMap(); + for (int i = 0; i < 1000; i++) { + String name = "test-" + index + "-property-" + i; + String value = name + "-value"; + map.put(name, value); + } + return map; + } + + @Override + public Origin getOrigin(String key) { + return null; + } + + @Override + public boolean isImmutable() { + return this.immutable; + } + + } + }