diff --git a/spring-core/src/jmh/java/org/springframework/core/env/CompositePropertySourceBenchmark.java b/spring-core/src/jmh/java/org/springframework/core/env/CompositePropertySourceBenchmark.java new file mode 100644 index 00000000000..3d5eae2bdf5 --- /dev/null +++ b/spring-core/src/jmh/java/org/springframework/core/env/CompositePropertySourceBenchmark.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2023 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.core.env; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import org.springframework.util.AlternativeJdkIdGenerator; +import org.springframework.util.IdGenerator; + +/** + * Benchmarks for {@link CompositePropertySource}. + * + * @author Yike Xiao + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 2) +@Measurement(iterations = 5, time = 2) +public class CompositePropertySourceBenchmark { + + @Benchmark + public void getPropertyNames(BenchmarkState state, Blackhole blackhole) { + blackhole.consume(state.composite.getPropertyNames()); + } + + @State(Scope.Benchmark) + public static class BenchmarkState { + + private static final IdGenerator ID_GENERATOR = new AlternativeJdkIdGenerator(); + + private static final Object VALUE = new Object(); + + CompositePropertySource composite; + + @Param({ "2", "5", "10" }) + int numberOfPropertySource; + + @Param({ "10", "100", "1000" }) + int numberOfPropertyNamesPerSource; + + @Setup(Level.Trial) + public void setUp() { + this.composite = new CompositePropertySource("benchmark"); + for (int i = 0; i < this.numberOfPropertySource; i++) { + Map map = new HashMap<>(this.numberOfPropertyNamesPerSource); + for (int j = 0; j < this.numberOfPropertyNamesPerSource; j++) { + map.put(ID_GENERATOR.generateId().toString(), VALUE); + } + PropertySource propertySource = new MapPropertySource("propertySource" + i, map); + this.composite.addPropertySource(propertySource); + } + } + + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/env/CompositePropertySource.java b/spring-core/src/main/java/org/springframework/core/env/CompositePropertySource.java index 6224a95f339..7345ff4dff9 100644 --- a/spring-core/src/main/java/org/springframework/core/env/CompositePropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/CompositePropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -78,15 +78,20 @@ public class CompositePropertySource extends EnumerablePropertySource { @Override public String[] getPropertyNames() { - Set names = new LinkedHashSet<>(); + List namesList = new ArrayList<>(this.propertySources.size()); + int total = 0; for (PropertySource propertySource : this.propertySources) { if (!(propertySource instanceof EnumerablePropertySource enumerablePropertySource)) { throw new IllegalStateException( "Failed to enumerate property names due to non-enumerable property source: " + propertySource); } - names.addAll(Arrays.asList(enumerablePropertySource.getPropertyNames())); + String[] names = enumerablePropertySource.getPropertyNames(); + namesList.add(names); + total += names.length; } - return StringUtils.toStringArray(names); + Set allNames = new LinkedHashSet<>(total); + namesList.forEach(names -> allNames.addAll(Arrays.asList(names))); + return StringUtils.toStringArray(allNames); } diff --git a/spring-core/src/test/java/org/springframework/core/env/CompositePropertySourceTests.java b/spring-core/src/test/java/org/springframework/core/env/CompositePropertySourceTests.java index d7bafe37643..fba8bd2aa5d 100644 --- a/spring-core/src/test/java/org/springframework/core/env/CompositePropertySourceTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/CompositePropertySourceTests.java @@ -17,6 +17,7 @@ package org.springframework.core.env; import java.util.Collections; +import java.util.Map; import org.junit.jupiter.api.Test; @@ -46,4 +47,13 @@ class CompositePropertySourceTests { assertThat(((i1 < i2) && (i2 < i3))).as("Bad order: " + s).isTrue(); } + @Test + void getPropertyNamesRemovesDuplicates() { + CompositePropertySource composite = new CompositePropertySource("c"); + composite.addPropertySource(new MapPropertySource("p1", Map.of("p1.property", "value"))); + composite.addPropertySource(new MapPropertySource("p2", + Map.of("p2.property1", "value", "p1.property", "value", "p2.property2", "value"))); + assertThat(composite.getPropertyNames()).containsOnly("p1.property", "p2.property1", "p2.property2"); + } + }