From 2956b860359c465b8c01775d227292cb6736722b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 23 Jan 2019 14:07:36 +0000 Subject: [PATCH 1/4] Avoid creating ConfigurationPropertyName just to get its Elements See gh-15760 --- .../source/ConfigurationPropertyName.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java index 6c798ad8332..63bd5b151ac 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -195,7 +195,7 @@ public final class ConfigurationPropertyName if (elementValue == null) { return this; } - Elements additionalElements = of(elementValue).elements; + Elements additionalElements = elementsOf(elementValue); return new ConfigurationPropertyName(this.elements.append(additionalElements)); } @@ -420,12 +420,21 @@ public final class ConfigurationPropertyName * {@code returnNullIfInvalid} is {@code false} */ static ConfigurationPropertyName of(CharSequence name, boolean returnNullIfInvalid) { + Elements elements = elementsOf(name, returnNullIfInvalid); + return (elements != null) ? new ConfigurationPropertyName(elements) : null; + } + + private static Elements elementsOf(CharSequence name) { + return elementsOf(name, false); + } + + private static Elements elementsOf(CharSequence name, boolean returnNullIfInvalid) { if (name == null) { Assert.isTrue(returnNullIfInvalid, "Name must not be null"); return null; } if (name.length() == 0) { - return EMPTY; + return Elements.EMPTY; } if (name.charAt(0) == '.' || name.charAt(name.length() - 1) == '.') { if (returnNullIfInvalid) { @@ -444,7 +453,7 @@ public final class ConfigurationPropertyName getInvalidChars(elements, i)); } } - return new ConfigurationPropertyName(elements); + return elements; } private static List getInvalidChars(Elements elements, int index) { From 8ec6c372e4333c1e3529b77a723e5d5946a0b7b7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 23 Jan 2019 14:09:03 +0000 Subject: [PATCH 2/4] Do not expand ElementsParser until size equals storage length Previously, ElementsParser would expand its internal storage when the size of the storage was <= the end index of the element being parsed, irrespective of how many elements had been stored. This led to expansion of the storage, even for a source that contains a single element, if the end of the element was at an index greater than the size of the storage. This commit updates ElementsParser to resize its storage when the size (the number of elements that have been stored) is equal to the size of the storage. See gh-15760 --- .../context/properties/source/ConfigurationPropertyName.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java index 63bd5b151ac..d1af209d520 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java @@ -798,7 +798,7 @@ public final class ConfigurationPropertyName if ((end - start) < 1 || type == ElementType.EMPTY) { return; } - if (this.start.length <= end) { + if (this.start.length == this.size) { this.start = expand(this.start); this.end = expand(this.end); this.type = expand(this.type); From 39e2aaa41c447b31ad934edb18bd95ec81be2b86 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 23 Jan 2019 14:41:39 +0000 Subject: [PATCH 3/4] Size the ElementsParser based on expected number of elements Previously, the ElementsParser would be created using its default capacity of 6 even when parsing a String that is expected to produce a single element. This commit updates ConfigurationPropertyName to create an ElementsParser with a capacity of 1 when parsing a String that should contain only a single element. See gh-15760 --- .../source/ConfigurationPropertyName.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java index d1af209d520..0af5f76fdc5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java @@ -195,7 +195,7 @@ public final class ConfigurationPropertyName if (elementValue == null) { return this; } - Elements additionalElements = elementsOf(elementValue); + Elements additionalElements = probablySingleElementOf(elementValue); return new ConfigurationPropertyName(this.elements.append(additionalElements)); } @@ -424,11 +424,16 @@ public final class ConfigurationPropertyName return (elements != null) ? new ConfigurationPropertyName(elements) : null; } - private static Elements elementsOf(CharSequence name) { - return elementsOf(name, false); + private static Elements probablySingleElementOf(CharSequence name) { + return elementsOf(name, false, 1); } private static Elements elementsOf(CharSequence name, boolean returnNullIfInvalid) { + return elementsOf(name, returnNullIfInvalid, ElementsParser.DEFAULT_CAPACITY); + } + + private static Elements elementsOf(CharSequence name, boolean returnNullIfInvalid, + int parserCapacity) { if (name == null) { Assert.isTrue(returnNullIfInvalid, "Name must not be null"); return null; @@ -443,7 +448,7 @@ public final class ConfigurationPropertyName throw new InvalidConfigurationPropertyNameException(name, Collections.singletonList('.')); } - Elements elements = new ElementsParser(name, '.').parse(); + Elements elements = new ElementsParser(name, '.', parserCapacity).parse(); for (int i = 0; i < elements.getSize(); i++) { if (elements.getType(i) == ElementType.NON_UNIFORM) { if (returnNullIfInvalid) { From 47b378e37361ad9709fdfd11e398820843820a95 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 23 Jan 2019 14:49:16 +0000 Subject: [PATCH 4/4] Derive StringBuilder's size from number of elements Previously, when ConfigurationPropertyName was building the String returned from toString() it would use a StringBuilder with the default initial capacity of 16. For properties with several elements this was likely to be too small resulting in the builder's buffer being resized. This commit sizes the StringBuilder as a multiple of the number of elements in the name, attempting to strike a balance between allocating a StringBuilder with an initial capacity that's too large and wastes memory and an initial capacity that's too small and requires resizing. See gh-15760 --- .../context/properties/source/ConfigurationPropertyName.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java index 0af5f76fdc5..ad3e5b3350c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java @@ -373,8 +373,9 @@ public final class ConfigurationPropertyName ElementType.DASHED)) { return this.elements.getSource().toString(); } - StringBuilder result = new StringBuilder(); - for (int i = 0; i < getNumberOfElements(); i++) { + int elements = getNumberOfElements(); + StringBuilder result = new StringBuilder(elements * 8); + for (int i = 0; i < elements; i++) { boolean indexed = isIndexed(i); if (result.length() > 0 && !indexed) { result.append('.');