Skip to content

Commit ae6908e

Browse files
committed
Cache property mappings in ConfigurationPropertyName
Relocate `SystemEnvironmentPropertyMapper` methods into `ConfigurationPropertyName` so that they can be cached to improve performance Closes gh-44858
1 parent 81dee54 commit ae6908e

File tree

2 files changed

+79
-62
lines changed

2 files changed

+79
-62
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java

+73-6
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
import java.util.Collection;
2121
import java.util.Collections;
2222
import java.util.List;
23+
import java.util.Locale;
2324
import java.util.Map;
2425
import java.util.function.Function;
26+
import java.util.function.IntFunction;
2527

2628
import org.springframework.util.Assert;
2729
import org.springframework.util.StringUtils;
@@ -63,10 +65,14 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
6365

6466
private final CharSequence[] uniformElements;
6567

66-
private String string;
67-
6868
private int hashCode;
6969

70+
private String[] string = new String[ToStringFormat.values().length];
71+
72+
private Boolean hasDashedElement;
73+
74+
private ConfigurationPropertyName systemEnvironmentLegacyName;
75+
7076
private ConfigurationPropertyName(Elements elements) {
7177
this.elements = elements;
7278
this.uniformElements = new CharSequence[elements.getSize()];
@@ -525,15 +531,41 @@ public int hashCode() {
525531
return hashCode;
526532
}
527533

534+
ConfigurationPropertyName asSystemEnvironmentLegacyName() {
535+
ConfigurationPropertyName name = this.systemEnvironmentLegacyName;
536+
if (name == null) {
537+
name = ConfigurationPropertyName
538+
.ofIfValid(buildSimpleToString('.', (i) -> getElement(i, Form.DASHED).replace('-', '.')));
539+
this.systemEnvironmentLegacyName = (name != null) ? name : EMPTY;
540+
}
541+
return (name != EMPTY) ? name : null;
542+
}
543+
528544
@Override
529545
public String toString() {
530-
if (this.string == null) {
531-
this.string = buildToString();
546+
return toString(ToStringFormat.DEFAULT);
547+
}
548+
549+
String toString(ToStringFormat format) {
550+
String string = this.string[format.ordinal()];
551+
if (string == null) {
552+
string = buildToString(format);
553+
this.string[format.ordinal()] = string;
532554
}
533-
return this.string;
555+
return string;
556+
}
557+
558+
private String buildToString(ToStringFormat format) {
559+
return switch (format) {
560+
case DEFAULT -> buildDefaultToString();
561+
case SYSTEM_ENVIRONMENT ->
562+
buildSimpleToString('_', (i) -> getElement(i, Form.UNIFORM).toUpperCase(Locale.ENGLISH));
563+
case LEGACY_SYSTEM_ENVIRONMENT -> buildSimpleToString('_',
564+
(i) -> getElement(i, Form.ORIGINAL).replace('-', '_').toUpperCase(Locale.ENGLISH));
565+
};
534566
}
535567

536-
private String buildToString() {
568+
private String buildDefaultToString() {
537569
if (this.elements.canShortcutWithSource(ElementType.UNIFORM, ElementType.DASHED)) {
538570
return this.elements.getSource().toString();
539571
}
@@ -556,6 +588,32 @@ private String buildToString() {
556588
return result.toString();
557589
}
558590

591+
private String buildSimpleToString(char joinChar, IntFunction<String> elementConverter) {
592+
StringBuilder result = new StringBuilder();
593+
for (int i = 0; i < getNumberOfElements(); i++) {
594+
if (!result.isEmpty()) {
595+
result.append(joinChar);
596+
}
597+
result.append(elementConverter.apply(i));
598+
}
599+
return result.toString();
600+
}
601+
602+
boolean hasDashedElement() {
603+
Boolean hasDashedElement = this.hasDashedElement;
604+
if (hasDashedElement != null) {
605+
return hasDashedElement;
606+
}
607+
for (int i = 0; i < getNumberOfElements(); i++) {
608+
if (getElement(i, Form.DASHED).indexOf('-') != -1) {
609+
this.hasDashedElement = true;
610+
return true;
611+
}
612+
}
613+
this.hasDashedElement = false;
614+
return false;
615+
}
616+
559617
/**
560618
* Returns if the given name is valid. If this method returns {@code true} then the
561619
* name may be used with {@link #of(CharSequence)} without throwing an exception.
@@ -1132,4 +1190,13 @@ private interface ElementCharPredicate {
11321190

11331191
}
11341192

1193+
/**
1194+
* Formats for {@code toString}.
1195+
*/
1196+
enum ToStringFormat {
1197+
1198+
DEFAULT, SYSTEM_ENVIRONMENT, LEGACY_SYSTEM_ENVIRONMENT
1199+
1200+
}
1201+
11351202
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@
2222
import java.util.Locale;
2323
import java.util.function.BiPredicate;
2424

25-
import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form;
25+
import org.springframework.boot.context.properties.source.ConfigurationPropertyName.ToStringFormat;
2626

2727
/**
2828
* {@link PropertyMapper} for system environment variables. Names are mapped by removing
@@ -42,44 +42,14 @@ final class SystemEnvironmentPropertyMapper implements PropertyMapper {
4242

4343
@Override
4444
public List<String> map(ConfigurationPropertyName configurationPropertyName) {
45-
String name = convertName(configurationPropertyName);
46-
String legacyName = convertLegacyName(configurationPropertyName);
45+
String name = configurationPropertyName.toString(ToStringFormat.SYSTEM_ENVIRONMENT);
46+
String legacyName = configurationPropertyName.toString(ToStringFormat.LEGACY_SYSTEM_ENVIRONMENT);
4747
if (name.equals(legacyName)) {
4848
return Collections.singletonList(name);
4949
}
5050
return Arrays.asList(name, legacyName);
5151
}
5252

53-
private String convertName(ConfigurationPropertyName name) {
54-
return convertName(name, name.getNumberOfElements());
55-
}
56-
57-
private String convertName(ConfigurationPropertyName name, int numberOfElements) {
58-
StringBuilder result = new StringBuilder();
59-
for (int i = 0; i < numberOfElements; i++) {
60-
if (!result.isEmpty()) {
61-
result.append('_');
62-
}
63-
result.append(name.getElement(i, Form.UNIFORM).toUpperCase(Locale.ENGLISH));
64-
}
65-
return result.toString();
66-
}
67-
68-
private String convertLegacyName(ConfigurationPropertyName name) {
69-
StringBuilder result = new StringBuilder();
70-
for (int i = 0; i < name.getNumberOfElements(); i++) {
71-
if (!result.isEmpty()) {
72-
result.append('_');
73-
}
74-
result.append(convertLegacyNameElement(name.getElement(i, Form.ORIGINAL)));
75-
}
76-
return result.toString();
77-
}
78-
79-
private Object convertLegacyNameElement(String element) {
80-
return element.replace('-', '_').toUpperCase(Locale.ENGLISH);
81-
}
82-
8353
@Override
8454
public ConfigurationPropertyName map(String propertySourceName) {
8555
return convertName(propertySourceName);
@@ -113,31 +83,11 @@ private boolean isAncestorOf(ConfigurationPropertyName name, ConfigurationProper
11383
}
11484

11585
private boolean isLegacyAncestorOf(ConfigurationPropertyName name, ConfigurationPropertyName candidate) {
116-
if (!hasDashedEntries(name)) {
86+
if (!name.hasDashedElement()) {
11787
return false;
11888
}
119-
ConfigurationPropertyName legacyCompatibleName = buildLegacyCompatibleName(name);
89+
ConfigurationPropertyName legacyCompatibleName = name.asSystemEnvironmentLegacyName();
12090
return legacyCompatibleName != null && legacyCompatibleName.isAncestorOf(candidate);
12191
}
12292

123-
private ConfigurationPropertyName buildLegacyCompatibleName(ConfigurationPropertyName name) {
124-
StringBuilder legacyCompatibleName = new StringBuilder();
125-
for (int i = 0; i < name.getNumberOfElements(); i++) {
126-
if (i != 0) {
127-
legacyCompatibleName.append('.');
128-
}
129-
legacyCompatibleName.append(name.getElement(i, Form.DASHED).replace('-', '.'));
130-
}
131-
return ConfigurationPropertyName.ofIfValid(legacyCompatibleName);
132-
}
133-
134-
boolean hasDashedEntries(ConfigurationPropertyName name) {
135-
for (int i = 0; i < name.getNumberOfElements(); i++) {
136-
if (name.getElement(i, Form.DASHED).indexOf('-') != -1) {
137-
return true;
138-
}
139-
}
140-
return false;
141-
}
142-
14393
}

0 commit comments

Comments
 (0)