Skip to content

Commit a4f6465

Browse files
Use ResolvableType to back TypeInformation.
1 parent cde4097 commit a4f6465

16 files changed

+494
-1248
lines changed

Diff for: src/main/java/org/springframework/data/repository/query/Parameter.java

+9-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Collections;
2424
import java.util.List;
2525
import java.util.Optional;
26+
import java.util.stream.Stream;
2627

2728
import org.springframework.core.MethodParameter;
2829
import org.springframework.core.ResolvableType;
@@ -34,6 +35,7 @@
3435
import org.springframework.data.util.ClassTypeInformation;
3536
import org.springframework.data.util.Lazy;
3637
import org.springframework.data.util.TypeInformation;
38+
import org.springframework.data.util.TypeDiscoverer;
3739
import org.springframework.util.Assert;
3840

3941
/**
@@ -210,24 +212,17 @@ boolean isSort() {
210212
*/
211213
private static boolean isDynamicProjectionParameter(MethodParameter parameter) {
212214

213-
Method method = parameter.getMethod();
214-
215-
if (method == null) {
216-
throw new IllegalStateException(String.format("Method parameter %s is not backed by a method!", parameter));
217-
}
218-
219-
ClassTypeInformation<?> ownerType = ClassTypeInformation.from(parameter.getDeclaringClass());
220-
TypeInformation<?> parameterTypes = ownerType.getParameterTypes(method).get(parameter.getParameterIndex());
221-
222-
if (!parameterTypes.getType().equals(Class.class)) {
215+
if (!parameter.getParameterType().equals(Class.class)) {
223216
return false;
224217
}
225218

226-
TypeInformation<?> bound = parameterTypes.getTypeArguments().get(0);
227-
TypeInformation<Object> returnType = ClassTypeInformation.fromReturnTypeOf(method);
219+
ResolvableType returnType = ResolvableType.forMethodReturnType(parameter.getMethod());
220+
if(new TypeDiscoverer(returnType).isCollectionLike() || org.springframework.util.ClassUtils.isAssignable(Stream.class, returnType.toClass())) {
221+
returnType = returnType.getGeneric(0);
222+
}
228223

229-
return bound
230-
.equals(QueryExecutionConverters.unwrapWrapperTypes(ReactiveWrapperConverters.unwrapWrapperTypes(returnType)));
224+
ResolvableType type = ResolvableType.forMethodParameter(parameter);
225+
return returnType.getType().equals(type.getGeneric(0).getType());
231226
}
232227

233228
/**

Diff for: src/main/java/org/springframework/data/repository/query/QueryMethod.java

+1
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ private static void assertReturnTypeAssignable(Method method, Set<Class<?>> type
299299
Assert.notNull(method, "Method must not be null!");
300300
Assert.notEmpty(types, "Types must not be null or empty!");
301301

302+
// TODO: to resolve generics fully we'd need the actual repository interface here
302303
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
303304

304305
returnType = QueryExecutionConverters.isSingleValue(returnType.getType()) //

Diff for: src/main/java/org/springframework/data/repository/util/ClassUtils.java

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ public static void unwrapReflectionException(Exception ex) throws Throwable {
191191
throw ex;
192192
}
193193

194+
// TODO: we should also consider having the owning type here so we can resolve generics better.
194195
private static TypeInformation<?> getEffectivelyReturnedTypeFrom(Method method) {
195196

196197
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);

Diff for: src/main/java/org/springframework/data/util/ClassTypeInformation.java

+30-79
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,15 @@
1616
package org.springframework.data.util;
1717

1818
import java.lang.reflect.Method;
19-
import java.lang.reflect.Type;
20-
import java.lang.reflect.TypeVariable;
2119
import java.util.Arrays;
2220
import java.util.Collection;
23-
import java.util.Collections;
24-
import java.util.HashMap;
25-
import java.util.HashSet;
2621
import java.util.List;
2722
import java.util.Map;
2823
import java.util.Set;
2924

30-
import org.springframework.core.GenericTypeResolver;
3125
import org.springframework.core.convert.TypeDescriptor;
26+
import org.springframework.core.ResolvableType;
27+
import org.springframework.lang.Nullable;
3228
import org.springframework.util.Assert;
3329
import org.springframework.util.ConcurrentReferenceHashMap;
3430
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
@@ -39,7 +35,6 @@
3935
* @author Oliver Gierke
4036
* @author Christoph Strobl
4137
*/
42-
@SuppressWarnings({ "unchecked", "rawtypes" })
4338
public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
4439

4540
public static final ClassTypeInformation<Collection> COLLECTION = new ClassTypeInformation(Collection.class);
@@ -51,95 +46,51 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
5146
private static final Map<Class<?>, ClassTypeInformation<?>> cache = new ConcurrentReferenceHashMap<>(64,
5247
ReferenceType.WEAK);
5348

54-
static {
55-
Arrays.asList(COLLECTION, LIST, SET, MAP, OBJECT).forEach(it -> cache.put(it.getType(), it));
56-
}
57-
58-
private final Class<S> type;
59-
private final Lazy<TypeDescriptor> descriptor;
60-
6149
/**
62-
* Simple factory method to easily create new instances of {@link ClassTypeInformation}.
63-
*
64-
* @param <S>
65-
* @param type must not be {@literal null}.
50+
* Warning: Does not fully resolve generic arguments.
51+
* @param method
6652
* @return
53+
* @deprecated since 3.0 Use {@link #fromReturnTypeOf(Method, Class)} instead.
6754
*/
68-
public static <S> ClassTypeInformation<S> from(Class<S> type) {
69-
70-
Assert.notNull(type, "Type must not be null!");
71-
72-
return (ClassTypeInformation<S>) cache.computeIfAbsent(type, ClassTypeInformation::new);
55+
@Deprecated
56+
public static TypeInformation<?> fromReturnTypeOf(Method method) {
57+
return new TypeDiscoverer<>(ResolvableType.forMethodReturnType(method));
7358
}
7459

7560
/**
76-
* Creates a {@link TypeInformation} from the given method's return type.
77-
*
78-
* @param method must not be {@literal null}.
61+
* @param method
62+
* @param actualType can be {@literal null}.
7963
* @return
8064
*/
81-
public static <S> TypeInformation<S> fromReturnTypeOf(Method method) {
65+
public static TypeInformation<?> fromReturnTypeOf(Method method, @Nullable Class<?> actualType) {
8266

83-
Assert.notNull(method, "Method must not be null!");
84-
return (TypeInformation<S>) ClassTypeInformation.from(method.getDeclaringClass())
85-
.createInfo(method.getGenericReturnType());
67+
if(actualType == null) {
68+
return new TypeDiscoverer<>(ResolvableType.forMethodReturnType(method));
69+
}
70+
return new TypeDiscoverer<>(ResolvableType.forMethodReturnType(method, actualType));
8671
}
8772

88-
/**
89-
* Creates {@link ClassTypeInformation} for the given type.
90-
*
91-
* @param type
92-
*/
93-
ClassTypeInformation(Class<S> type) {
94-
95-
super(type, getTypeVariableMap(type));
73+
Class<?> type;
9674

97-
this.type = type;
98-
this.descriptor = Lazy.of(() -> TypeDescriptor.valueOf(type));
99-
}
100-
101-
/**
102-
* Little helper to allow us to create a generified map, actually just to satisfy the compiler.
103-
*
104-
* @param type must not be {@literal null}.
105-
* @return
106-
*/
107-
private static Map<TypeVariable<?>, Type> getTypeVariableMap(Class<?> type) {
108-
return getTypeVariableMap(type, new HashSet<>());
75+
static {
76+
Arrays.asList(COLLECTION, LIST, SET, MAP, OBJECT).forEach(it -> cache.put(it.getType(), it));
10977
}
11078

111-
private static Map<TypeVariable<?>, Type> getTypeVariableMap(Class<?> type, Collection<Type> visited) {
112-
113-
if (visited.contains(type)) {
114-
return Collections.emptyMap();
115-
} else {
116-
visited.add(type);
117-
}
118-
119-
Map<TypeVariable, Type> source = GenericTypeResolver.getTypeVariableMap(type);
120-
Map<TypeVariable<?>, Type> map = new HashMap<>(source.size());
121-
122-
for (Map.Entry<TypeVariable, Type> entry : source.entrySet()) {
123-
124-
Type value = entry.getValue();
125-
map.put(entry.getKey(), entry.getValue());
79+
public static <S> ClassTypeInformation<S> from(Class<S> type) {
12680

127-
if (value instanceof Class) {
81+
Assert.notNull(type, "Type must not be null!");
12882

129-
for (Map.Entry<TypeVariable<?>, Type> nestedEntry : getTypeVariableMap((Class<?>) value, visited).entrySet()) {
130-
if (!map.containsKey(nestedEntry.getKey())) {
131-
map.put(nestedEntry.getKey(), nestedEntry.getValue());
132-
}
133-
}
134-
}
135-
}
83+
return (ClassTypeInformation<S>) cache.computeIfAbsent(type, ClassTypeInformation::new);
84+
}
13685

137-
return map;
86+
ClassTypeInformation(Class<S> type) {
87+
super(ResolvableType.forClass(type));
88+
this.type = type;
13889
}
13990

14091
@Override
14192
public Class<S> getType() {
142-
return type;
93+
return (Class<S>) type;
14394
}
14495

14596
@Override
@@ -158,12 +109,12 @@ public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
158109
}
159110

160111
@Override
161-
public TypeDescriptor toTypeDescriptor() {
162-
return descriptor.get();
112+
public String toString() {
113+
return type.getName();
163114
}
164115

165116
@Override
166-
public String toString() {
167-
return type.getName();
117+
public boolean equals(Object o) {
118+
return super.equals(o);
168119
}
169120
}

Diff for: src/main/java/org/springframework/data/util/GenericArrayTypeInformation.java

-64
This file was deleted.

0 commit comments

Comments
 (0)