Skip to content

Commit b313dfe

Browse files
committed
Revise lambda caching.
Remove lambda cache in favor of resolved property references/PropertyPaths.
1 parent ef595f0 commit b313dfe

File tree

4 files changed

+67
-90
lines changed

4 files changed

+67
-90
lines changed

src/main/java/org/springframework/data/core/PropertyReference.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,15 @@ public interface PropertyReference<T, P extends @Nullable Object> extends Serial
8181
* Syntax sugar to create a {@link PropertyReference} from a method reference or lambda for a collection property.
8282
* <p>
8383
* This method returns a resolved {@link PropertyReference} by introspecting the given method reference or lambda.
84+
* Note that {@link #get(Object)} becomes unusable for collection properties as the property type adapted from
85+
* {@code Iterable &lt;P&gt;} and a single {@code P} cannot represent a collection of items.
8486
*
8587
* @param property the method reference or lambda.
8688
* @param <T> owning type.
8789
* @param <P> property type.
8890
* @return the typed property reference.
8991
*/
92+
@SuppressWarnings({ "unchecked", "rawtypes" })
9093
static <T, P> PropertyReference<T, P> ofMany(PropertyReference<T, ? extends Iterable<P>> property) {
9194
return (PropertyReference) PropertyReferences.of(property);
9295
}
@@ -106,7 +109,7 @@ static <T, P> PropertyReference<T, P> ofMany(PropertyReference<T, ? extends Iter
106109
* @return the owningType will never be {@literal null}.
107110
*/
108111
default TypeInformation<?> getOwningType() {
109-
return PropertyReferences.getMetadata(this).owner();
112+
return PropertyReferences.of(this).getOwningType();
110113
}
111114

112115
/**
@@ -115,7 +118,7 @@ default TypeInformation<?> getOwningType() {
115118
* @return the current property name.
116119
*/
117120
default String getName() {
118-
return PropertyReferences.getMetadata(this).property();
121+
return PropertyReferences.of(this).getName();
119122
}
120123

121124
/**
@@ -136,7 +139,7 @@ default Class<?> getType() {
136139
* @return the type information for the property at this segment.
137140
*/
138141
default TypeInformation<?> getTypeInformation() {
139-
return PropertyReferences.getMetadata(this).propertyType();
142+
return PropertyReferences.of(this).getTypeInformation();
140143
}
141144

142145
/**
@@ -166,12 +169,16 @@ default boolean isCollection() {
166169
/**
167170
* Extend the property to a property path by appending the {@code next} path segment and return a new property path
168171
* instance.
172+
* <p>
173+
* Note that {@link #get(Object)} becomes unusable for collection properties as the property type adapted from
174+
* {@code Iterable &lt;P&gt;} and a single {@code P} cannot represent a collection of items.
169175
*
170176
* @param next the next property path segment as method reference or lambda accepting the owner object {@code P} type
171177
* and returning {@code N} as result of accessing a property.
172178
* @param <N> the new property value type.
173179
* @return a new composed {@code TypedPropertyPath}.
174180
*/
181+
@SuppressWarnings({ "unchecked", "rawtypes" })
175182
default <N extends @Nullable Object> TypedPropertyPath<T, N> thenMany(
176183
PropertyReference<P, ? extends Iterable<N>> next) {
177184
return (TypedPropertyPath) TypedPropertyPaths.compose(this, next);

src/main/java/org/springframework/data/core/PropertyReferences.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
*/
4141
class PropertyReferences {
4242

43-
private static final Map<ClassLoader, Map<Object, PropertyReferenceMetadata>> lambdas = new WeakHashMap<>();
4443
private static final Map<ClassLoader, Map<PropertyReference<?, ?>, ResolvedPropertyReference<?, ?>>> resolved = new WeakHashMap<>();
4544

4645
private static final SerializableLambdaReader reader = new SerializableLambdaReader(PropertyReference.class,
@@ -62,7 +61,7 @@ public static <P, T> PropertyReference<T, P> of(PropertyReference<T, P> lambda)
6261
}
6362

6463
return (PropertyReference<T, P>) cache.computeIfAbsent(lambda,
65-
o -> new ResolvedPropertyReference(o, getMetadata(lambda)));
64+
o -> new ResolvedPropertyReference(o, read(lambda)));
6665
}
6766

6867
/**
@@ -79,19 +78,6 @@ public static <T, P> PropertyReference<T, P> of(PropertyReference<T, P> delegate
7978
return new ResolvedPropertyReference<>(delegate, metadata);
8079
}
8180

82-
/**
83-
* Retrieve {@link PropertyReferenceMetadata} for a given {@link PropertyReference}.
84-
*/
85-
public static PropertyReferenceMetadata getMetadata(PropertyReference<?, ?> lambda) {
86-
87-
Map<Object, PropertyReferenceMetadata> cache;
88-
synchronized (lambdas) {
89-
cache = lambdas.computeIfAbsent(lambda.getClass().getClassLoader(), k -> new ConcurrentReferenceHashMap<>());
90-
}
91-
92-
return cache.computeIfAbsent(lambda, o -> read(lambda));
93-
}
94-
9581
private static PropertyReferenceMetadata read(PropertyReference<?, ?> lambda) {
9682

9783
MemberDescriptor reference = reader.read(lambda);

src/main/java/org/springframework/data/core/TypedPropertyPath.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,16 @@ public interface TypedPropertyPath<T, P extends @Nullable Object> extends Proper
9797
* Syntax sugar to create a {@link TypedPropertyPath} from a method reference or lambda for a collection property.
9898
* <p>
9999
* This method returns a resolved {@link TypedPropertyPath} by introspecting the given method reference or lambda.
100+
* <p>
101+
* Note that {@link #get(Object)} becomes unusable for collection properties as the property type adapted from
102+
* {@code Iterable &lt;P&gt;} and a single {@code P} cannot represent a collection of items.
100103
*
101104
* @param propertyPath the method reference or lambda.
102105
* @param <T> owning type.
103106
* @param <P> property type.
104107
* @return the typed property path.
105-
* @since 4.1
106108
*/
109+
@SuppressWarnings({ "unchecked", "rawtypes" })
107110
static <T, P> TypedPropertyPath<T, P> ofMany(TypedPropertyPath<T, ? extends Iterable<P>> propertyPath) {
108111
return (TypedPropertyPath) TypedPropertyPaths.of(propertyPath);
109112
}
@@ -119,17 +122,17 @@ static <T, P> TypedPropertyPath<T, P> ofMany(TypedPropertyPath<T, ? extends Iter
119122

120123
@Override
121124
default TypeInformation<?> getOwningType() {
122-
return TypedPropertyPaths.getMetadata(this).owner();
125+
return TypedPropertyPaths.of(this).getOwningType();
123126
}
124127

125128
@Override
126129
default String getSegment() {
127-
return TypedPropertyPaths.getMetadata(this).property();
130+
return TypedPropertyPaths.of(this).getSegment();
128131
}
129132

130133
@Override
131134
default TypeInformation<?> getTypeInformation() {
132-
return TypedPropertyPaths.getMetadata(this).propertyType();
135+
return TypedPropertyPaths.of(this).getTypeInformation();
133136
}
134137

135138
@Override
@@ -162,12 +165,16 @@ default Iterator<PropertyPath> iterator() {
162165

163166
/**
164167
* Extend the property path by appending the {@code next} path segment and returning a new property path instance.
168+
* <p>
169+
* Note that {@link #get(Object)} becomes unusable for collection properties as the property type adapted from
170+
* {@code Iterable &lt;P&gt;} and a single {@code P} cannot represent a collection of items.
165171
*
166172
* @param next the next property path segment as method reference or lambda accepting the owner object {@code P} type
167173
* and returning {@code N} as result of accessing a property.
168174
* @param <N> the new property value type.
169175
* @return a new composed {@code TypedPropertyPath}.
170176
*/
177+
@SuppressWarnings({ "unchecked", "rawtypes" })
171178
default <N extends @Nullable Object> TypedPropertyPath<T, N> thenMany(
172179
PropertyReference<P, ? extends Iterable<N>> next) {
173180
return (TypedPropertyPath) TypedPropertyPaths.compose(this, PropertyReference.of(next));

src/main/java/org/springframework/data/core/TypedPropertyPaths.java

Lines changed: 45 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
*/
5353
class TypedPropertyPaths {
5454

55-
private static final Map<ClassLoader, Map<Object, PropertyPathMetadata>> lambdas = new WeakHashMap<>();
5655
private static final Map<ClassLoader, Map<Serializable, ResolvedTypedPropertyPath<?, ?>>> resolved = new WeakHashMap<>();
5756

5857
private static final SerializableLambdaReader reader = new SerializableLambdaReader(PropertyPath.class,
@@ -125,7 +124,7 @@ public static <P, T> TypedPropertyPath<T, P> of(PropertyReference<T, P> lambda)
125124
return new PropertyReferenceWrapper<>(resolved);
126125
}
127126

128-
PropertyPathMetadata metadata = getMetadata(lambda);
127+
PropertyPathMetadata metadata = read(lambda);
129128

130129
if (KotlinDetector.isKotlinReflectPresent()) {
131130
if (metadata instanceof KPropertyPathMetadata kMetadata
@@ -137,45 +136,6 @@ public static <P, T> TypedPropertyPath<T, P> of(PropertyReference<T, P> lambda)
137136
return new ResolvedPropertyReference<>(lambda, metadata);
138137
}
139138

140-
/**
141-
* Delegate to handle property path composition of single-property and property-path KProperty1 references.
142-
*/
143-
static class KotlinDelegate {
144-
145-
@SuppressWarnings({ "rawtypes", "unchecked" })
146-
public static <T, P> TypedPropertyPath<T, P> of(Object property) {
147-
148-
if (property instanceof KPropertyPath paths) {
149-
150-
TypedPropertyPath parent = of(paths.getProperty());
151-
TypedPropertyPath child = of(paths.getLeaf());
152-
153-
return TypedPropertyPaths.compose(parent, child);
154-
}
155-
156-
if (property instanceof KPropertyImpl impl) {
157-
158-
Class<?> owner = impl.getJavaField() != null ? impl.getJavaField().getDeclaringClass()
159-
: impl.getGetter().getCaller().getMember().getDeclaringClass();
160-
KPropertyPathMetadata metadata = TypedPropertyPaths.KPropertyPathMetadata
161-
.of(MemberDescriptor.KPropertyReferenceDescriptor.create(owner, (KProperty1) impl));
162-
return new TypedPropertyPaths.ResolvedKPropertyPath(metadata);
163-
}
164-
165-
if (property instanceof KProperty1 kProperty) {
166-
167-
if (kProperty.getGetter().getProperty() instanceof KProperty1Impl impl) {
168-
return of(impl);
169-
}
170-
171-
throw new IllegalArgumentException("Property " + kProperty.getName() + " is not a KProperty");
172-
}
173-
174-
throw new IllegalArgumentException("Property " + property + " is not a KProperty");
175-
}
176-
177-
}
178-
179139
/**
180140
* Introspect {@link TypedPropertyPath} and return an introspected {@link ResolvedTypedPropertyPath} variant.
181141
*/
@@ -193,14 +153,14 @@ public static <P, T> TypedPropertyPath<T, P> of(TypedPropertyPath<T, P> lambda)
193153
}
194154

195155
return (TypedPropertyPath) cache.computeIfAbsent(lambda,
196-
o -> new ResolvedTypedPropertyPath(o, doGetMetadata(lambda)));
156+
o -> new ResolvedTypedPropertyPath(o, read(lambda)));
197157
}
198158

199159
/**
200160
* Retrieve {@link PropertyPathMetadata} for a given {@link TypedPropertyPath}.
201161
*/
202162
@SuppressWarnings({ "unchecked", "rawtypes" })
203-
public static <T, P> TypedPropertyPath<T, P> of(TypedPropertyPath<T, P> delegate, PropertyPathMetadata metadata) {
163+
private static <T, P> TypedPropertyPath<T, P> aaa(TypedPropertyPath<T, P> delegate, PropertyPathMetadata metadata) {
204164

205165
if (KotlinDetector.isKotlinReflectPresent() && metadata instanceof KPropertyPathMetadata) {
206166
return new ResolvedKPropertyPath(((KPropertyPathMetadata) metadata).getProperty(), metadata);
@@ -209,31 +169,6 @@ public static <T, P> TypedPropertyPath<T, P> of(TypedPropertyPath<T, P> delegate
209169
return new ResolvedTypedPropertyPath<>(delegate, metadata);
210170
}
211171

212-
/**
213-
* Retrieve {@link PropertyPathMetadata} for a given {@link PropertyReference}.
214-
*/
215-
public static PropertyPathMetadata getMetadata(PropertyReference<?, ?> lambda) {
216-
return doGetMetadata(lambda);
217-
}
218-
219-
/**
220-
* Retrieve {@link PropertyPathMetadata} for a given {@link TypedPropertyPath}.
221-
*/
222-
public static PropertyPathMetadata getMetadata(TypedPropertyPath<?, ?> lambda) {
223-
return doGetMetadata(lambda);
224-
}
225-
226-
private static PropertyPathMetadata doGetMetadata(Object lambda) {
227-
228-
Map<Object, PropertyPathMetadata> cache;
229-
230-
synchronized (lambdas) {
231-
cache = lambdas.computeIfAbsent(lambda.getClass().getClassLoader(), k -> new ConcurrentReferenceHashMap<>());
232-
}
233-
234-
return cache.computeIfAbsent(lambda, o -> read(lambda));
235-
}
236-
237172
private static PropertyPathMetadata read(Object lambda) {
238173

239174
MemberDescriptor reference = reader.read(lambda);
@@ -363,6 +298,48 @@ public KProperty<?> getProperty() {
363298
}
364299
}
365300

301+
/**
302+
* Delegate to handle property path composition of single-property and property-path KProperty1 references.
303+
*/
304+
static class KotlinDelegate {
305+
306+
@SuppressWarnings({ "rawtypes", "unchecked" })
307+
public static <T, P> TypedPropertyPath<T, P> of(Object property) {
308+
309+
if (property instanceof KPropertyPath paths) {
310+
311+
TypedPropertyPath parent = of(paths.getProperty());
312+
TypedPropertyPath child = of(paths.getLeaf());
313+
314+
return TypedPropertyPaths.compose(parent, child);
315+
}
316+
317+
if (property instanceof KPropertyImpl impl) {
318+
319+
Class<?> owner = impl.getJavaField() != null ? impl.getJavaField().getDeclaringClass()
320+
: impl.getGetter().getCaller().getMember().getDeclaringClass();
321+
KPropertyPathMetadata metadata = TypedPropertyPaths.KPropertyPathMetadata
322+
.of(MemberDescriptor.KPropertyReferenceDescriptor.create(owner, (KProperty1) impl));
323+
return new TypedPropertyPaths.ResolvedKPropertyPath(metadata);
324+
}
325+
326+
if (property instanceof KProperty1 kProperty) {
327+
328+
if (kProperty.getGetter().getProperty() instanceof KProperty1Impl impl) {
329+
return of(impl);
330+
}
331+
332+
throw new IllegalArgumentException("Property " + kProperty.getName() + " is not a KProperty");
333+
}
334+
335+
throw new IllegalArgumentException("Property " + property + " is not a KProperty");
336+
}
337+
338+
}
339+
340+
/**
341+
* Marker interface to indicate a resolved and processed property path.
342+
*/
366343
interface Resolved {
367344

368345
}

0 commit comments

Comments
 (0)