Skip to content

Commit adc8e3f

Browse files
mp911dechristophstrobl
authored andcommitted
Revisit internal caching arrangements.
Closes: #3185 Original Pull Request: #3186
1 parent e0e881f commit adc8e3f

File tree

5 files changed

+97
-33
lines changed

5 files changed

+97
-33
lines changed

src/main/java/org/springframework/data/convert/CustomConversions.java

+20-17
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,14 @@
1616
package org.springframework.data.convert;
1717

1818
import java.lang.annotation.Annotation;
19-
import java.util.ArrayList;
20-
import java.util.Arrays;
21-
import java.util.Collection;
22-
import java.util.Collections;
23-
import java.util.HashSet;
24-
import java.util.LinkedHashSet;
25-
import java.util.List;
26-
import java.util.Map;
27-
import java.util.Optional;
28-
import java.util.Set;
29-
import java.util.concurrent.ConcurrentHashMap;
19+
import java.util.*;
3020
import java.util.function.Function;
3121
import java.util.function.Predicate;
3222
import java.util.stream.Collectors;
3323

3424
import org.apache.commons.logging.Log;
3525
import org.apache.commons.logging.LogFactory;
26+
3627
import org.springframework.core.GenericTypeResolver;
3728
import org.springframework.core.annotation.AnnotationUtils;
3829
import org.springframework.core.convert.converter.Converter;
@@ -119,7 +110,7 @@ public class CustomConversions {
119110
private final Function<ConvertiblePair, Class<?>> getRawWriteTarget = convertiblePair -> getCustomTarget(
120111
convertiblePair.getSourceType(), null, writingPairs);
121112

122-
@Nullable private final PropertyValueConversions propertyValueConversions;
113+
private final @Nullable PropertyValueConversions propertyValueConversions;
123114

124115
/**
125116
* @param converterConfiguration the {@link ConverterConfiguration} to apply.
@@ -506,7 +497,7 @@ private Class<?> getCustomTarget(Class<?> sourceType, @Nullable Class<?> targetT
506497
*/
507498
static class ConversionTargetsCache {
508499

509-
private final Map<Class<?>, TargetTypes> customReadTargetTypes = new ConcurrentHashMap<>();
500+
private Map<Class<?>, TargetTypes> customReadTargetTypes = new HashMap<>();
510501

511502
/**
512503
* Get or compute a target type given its {@code sourceType}. Returns a cached {@link Optional} if the value
@@ -536,7 +527,15 @@ public Class<?> computeIfAbsent(Class<?> sourceType, Function<ConvertiblePair, C
536527
public Class<?> computeIfAbsent(Class<?> sourceType, Class<?> targetType,
537528
Function<ConvertiblePair, Class<?>> mappingFunction) {
538529

539-
TargetTypes targetTypes = customReadTargetTypes.computeIfAbsent(sourceType, TargetTypes::new);
530+
TargetTypes targetTypes = customReadTargetTypes.get(sourceType);
531+
532+
if (targetTypes == null) {
533+
534+
Map<Class<?>, TargetTypes> customReadTargetTypes = new HashMap<>(this.customReadTargetTypes);
535+
targetTypes = new TargetTypes(sourceType);
536+
customReadTargetTypes.put(sourceType, targetTypes);
537+
this.customReadTargetTypes = customReadTargetTypes;
538+
}
540539

541540
return targetTypes.computeIfAbsent(targetType, mappingFunction);
542541
}
@@ -555,7 +554,7 @@ interface AbsentTargetTypeMarker {}
555554
static class TargetTypes {
556555

557556
private final Class<?> sourceType;
558-
private final Map<Class<?>, Class<?>> conversionTargets = new ConcurrentHashMap<>();
557+
private Map<Class<?>, Class<?>> conversionTargets = new HashMap<>();
559558

560559
TargetTypes(Class<?> sourceType) {
561560
this.sourceType = sourceType;
@@ -576,17 +575,21 @@ public Class<?> computeIfAbsent(Class<?> targetType, Function<ConvertiblePair, C
576575
Class<?> optionalTarget = conversionTargets.get(targetType);
577576

578577
if (optionalTarget == null) {
578+
579579
optionalTarget = mappingFunction.apply(new ConvertiblePair(sourceType, targetType));
580+
581+
Map<Class<?>, Class<?>> conversionTargets = new HashMap<>(this.conversionTargets);
580582
conversionTargets.put(targetType, optionalTarget == null ? Void.class : optionalTarget);
583+
this.conversionTargets = conversionTargets;
581584
}
582585

583586
return Void.class.equals(optionalTarget) ? null : optionalTarget;
584587
}
585588
}
586589

587590
/**
588-
* Value class tying together a {@link ConverterRegistration} and its {@link ConverterOrigin origin} to allow fine
589-
* grained registration based on store supported types.
591+
* Value class tying together a {@link ConverterRegistration} and its {@link ConverterOrigin origin} to allow
592+
* fine-grained registration based on store supported types.
590593
*
591594
* @since 2.3
592595
* @author Christoph Strobl

src/main/java/org/springframework/data/convert/SimplePropertyValueConversions.java

+50-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
package org.springframework.data.convert;
1717

1818
import java.util.ArrayList;
19+
import java.util.HashMap;
1920
import java.util.List;
21+
import java.util.Map;
2022
import java.util.Optional;
2123

2224
import org.springframework.beans.factory.InitializingBean;
@@ -51,6 +53,23 @@ public class SimplePropertyValueConversions implements PropertyValueConversions,
5153

5254
private @Nullable ValueConverterRegistry<?> valueConverterRegistry;
5355

56+
private Map<PersistentProperty<?>, PropertyValueConverter<?, ?, ?>> converterCache = new HashMap<>();
57+
58+
@SuppressWarnings("rawtypes")
59+
enum NoOpConverter implements PropertyValueConverter {
60+
INSTANCE;
61+
62+
@Override
63+
public Object read(Object value, ValueConversionContext context) {
64+
return null;
65+
}
66+
67+
@Override
68+
public Object write(Object value, ValueConversionContext context) {
69+
return null;
70+
}
71+
}
72+
5473
/**
5574
* Set the {@link PropertyValueConverterFactory} responsible for creating the actual {@link PropertyValueConverter}.
5675
*
@@ -129,20 +148,49 @@ public void setConverterCacheEnabled(boolean converterCacheEnabled) {
129148
*/
130149
@Override
131150
public boolean hasValueConverter(PersistentProperty<?> property) {
132-
return requireConverterFactory().getConverter(property) != null;
151+
return doGetConverter(property) != null;
133152
}
134153

135154
@Override
136155
public <DV, SV, P extends PersistentProperty<P>, D extends ValueConversionContext<P>> PropertyValueConverter<DV, SV, D> getValueConverter(
137156
P property) {
138157

139-
PropertyValueConverter<DV, SV, D> converter = requireConverterFactory().getConverter(property);
158+
PropertyValueConverter<DV, SV, D> converter = doGetConverter(property);
140159

141160
Assert.notNull(converter, String.format("No PropertyValueConverter registered for %s", property));
142161

143162
return converter;
144163
}
145164

165+
@SuppressWarnings("unchecked")
166+
@Nullable
167+
private <DV, SV, P extends PersistentProperty<P>, D extends ValueConversionContext<P>> PropertyValueConverter<DV, SV, D> doGetConverter(
168+
PersistentProperty<?> property) {
169+
170+
PropertyValueConverter<?, ?, ?> converter = converterCache.get(property);
171+
172+
if (converter == null) {
173+
174+
converter = requireConverterFactory().getConverter(property);
175+
176+
Map<PersistentProperty<?>, PropertyValueConverter<?, ?, ?>> converterCache = new HashMap<>(this.converterCache);
177+
178+
if (converter == null) {
179+
converterCache.put(property, NoOpConverter.INSTANCE);
180+
} else {
181+
converterCache.put(property, converter);
182+
}
183+
184+
this.converterCache = converterCache;
185+
}
186+
187+
if (converter == NoOpConverter.INSTANCE) {
188+
return null;
189+
}
190+
191+
return (PropertyValueConverter<DV, SV, D>) converter;
192+
}
193+
146194
/**
147195
* May be called just once to initialize the underlying factory with its values.
148196
*/

src/main/java/org/springframework/data/mapping/InstanceCreatorMetadataSupport.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717

1818
import java.lang.reflect.Executable;
1919
import java.util.Arrays;
20+
import java.util.HashMap;
2021
import java.util.List;
2122
import java.util.Map;
22-
import java.util.concurrent.ConcurrentHashMap;
2323

2424
import org.springframework.util.Assert;
2525

@@ -35,7 +35,7 @@ class InstanceCreatorMetadataSupport<T, P extends PersistentProperty<P>> impleme
3535

3636
private final Executable executable;
3737
private final List<Parameter<Object, P>> parameters;
38-
private final Map<PersistentProperty<?>, Boolean> isPropertyParameterCache = new ConcurrentHashMap<>();
38+
private Map<PersistentProperty<?>, Boolean> isPropertyParameterCache = new HashMap<>();
3939

4040
/**
4141
* Creates a new {@link InstanceCreatorMetadataSupport} from the given {@link Executable} and {@link Parameter}s.
@@ -96,7 +96,9 @@ public boolean isCreatorParameter(PersistentProperty<?> property) {
9696

9797
boolean result = doGetIsCreatorParameter(property);
9898

99+
Map<PersistentProperty<?>, Boolean> isPropertyParameterCache = new HashMap<>(this.isPropertyParameterCache);
99100
isPropertyParameterCache.put(property, result);
101+
this.isPropertyParameterCache = isPropertyParameterCache;
100102

101103
return result;
102104
}

src/main/java/org/springframework/data/util/ClassTypeInformation.java

+11-7
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,19 @@
3131
*
3232
* @author Oliver Gierke
3333
* @author Christoph Strobl
34+
* @author Mark Paluch
3435
* @deprecated since 3.0 to go package protected at some point. Refer to {@link TypeInformation} only.
3536
*/
3637
@Deprecated(since = "3.0", forRemoval = true)
3738
@SuppressWarnings({ "rawtypes", "unchecked" })
3839
public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
3940

40-
private static final ConcurrentLruCache<ResolvableType, ClassTypeInformation<?>> cache = new ConcurrentLruCache<>(64,
41+
private static final ConcurrentLruCache<ResolvableType, ClassTypeInformation<?>> cache = new ConcurrentLruCache<>(128,
4142
ClassTypeInformation::new);
4243

44+
private static final ConcurrentLruCache<Class<?>, ResolvableType> resolvableTypeCache = new ConcurrentLruCache<>(128,
45+
ResolvableType::forClass);
46+
4347
public static final ClassTypeInformation<Collection> COLLECTION;
4448
public static final ClassTypeInformation<List> LIST;
4549
public static final ClassTypeInformation<Set> SET;
@@ -48,11 +52,11 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
4852

4953
static {
5054

51-
OBJECT = (ClassTypeInformation<Object>) cache.get(ResolvableType.forClass(Object.class));
52-
COLLECTION = (ClassTypeInformation<Collection>) cache.get(ResolvableType.forClass(Collection.class));
53-
LIST = (ClassTypeInformation<List>) cache.get(ResolvableType.forClass(List.class));
54-
SET = (ClassTypeInformation<Set>) cache.get(ResolvableType.forClass(Set.class));
55-
MAP = (ClassTypeInformation<Map>) cache.get(ResolvableType.forClass(Map.class));
55+
OBJECT = from(Object.class);
56+
COLLECTION = from(Collection.class);
57+
LIST = from(List.class);
58+
SET = from(Set.class);
59+
MAP = from(Map.class);
5660
}
5761

5862
private final Class<S> type;
@@ -70,7 +74,7 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
7074
*/
7175
@Deprecated
7276
public static <S> ClassTypeInformation<S> from(Class<S> type) {
73-
return from(ResolvableType.forClass(type));
77+
return from(resolvableTypeCache.get(type));
7478
}
7579

7680
static <S> ClassTypeInformation<S> from(ResolvableType type) {

src/main/java/org/springframework/data/util/TypeDiscoverer.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
5555

5656
private final ResolvableType resolvableType;
5757
private final Map<String, Optional<TypeInformation<?>>> fields = new ConcurrentHashMap<>();
58+
private final Lazy<Boolean> isMap = Lazy.of(() -> CustomCollections.isMap(getType()));
59+
private final Lazy<Boolean> isCollectionLike = Lazy.of(() -> {
60+
61+
Class<S> type = getType();
62+
63+
return type.isArray() //
64+
|| Iterable.class.equals(type) //
65+
|| Collection.class.isAssignableFrom(type) //
66+
|| Streamable.class.isAssignableFrom(type) || CustomCollections.isCollection(type);
67+
});
5868
private final Lazy<TypeInformation<?>> componentType;
5969
private final Lazy<TypeInformation<?>> valueType;
6070
private final Map<Constructor<?>, List<TypeInformation<?>>> constructorParameters = new ConcurrentHashMap<>();
@@ -125,10 +135,7 @@ public boolean isCollectionLike() {
125135

126136
Class<S> type = getType();
127137

128-
return type.isArray() //
129-
|| Iterable.class.equals(type) //
130-
|| Collection.class.isAssignableFrom(type) //
131-
|| Streamable.class.isAssignableFrom(type) || CustomCollections.isCollection(type);
138+
return isCollectionLike.get();
132139
}
133140

134141
@Nullable
@@ -169,7 +176,7 @@ protected TypeInformation<?> doGetComponentType() {
169176

170177
@Override
171178
public boolean isMap() {
172-
return CustomCollections.isMap(getType());
179+
return isMap.get();
173180
}
174181

175182
@Nullable

0 commit comments

Comments
 (0)