Skip to content

Commit 3e9451c

Browse files
committed
Polishing for full type information on RepositoryMetadata.
Avoid leaking internals from TypeInformation by exposing a ….toTypeDescriptor() method in TypeInformation directly. This causes the ResolvableType handling to become an implementation detail within ClassTypeInformation and avoid client code to manually deal with both ResolvableType and TypeDescriptor creation. Changed MethodLookups back to use the resolved domain types as the advanced generics resolutions seems not to be needed here and we can avoid Fixes #2518.
1 parent 58ed9ce commit 3e9451c

12 files changed

+117
-109
lines changed

src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,6 @@
3131
*/
3232
public interface RepositoryMetadata {
3333

34-
/**
35-
* Returns the id {@link TypeInformation} the given class is declared for.
36-
*
37-
* @return the {@link TypeInformation} class of the entity managed by the repository.
38-
*/
39-
TypeInformation<?> getIdTypeInformation();
40-
41-
/**
42-
* Returns the domain {@link TypeInformation} the repository is declared for.
43-
*
44-
* @return the domain class the repository is handling.
45-
*/
46-
TypeInformation<?> getDomainTypeInformation();
47-
48-
/**
49-
* Returns the repository interface.
50-
*
51-
* @return
52-
*/
53-
Class<?> getRepositoryInterface();
54-
5534
/**
5635
* Returns the raw id class the given class is declared for.
5736
*
@@ -60,7 +39,7 @@ public interface RepositoryMetadata {
6039
default Class<?> getIdType() {
6140
return getIdTypeInformation().getType();
6241
}
63-
42+
6443
/**
6544
* Returns the raw domain class the repository is declared for.
6645
*
@@ -70,6 +49,31 @@ default Class<?> getDomainType() {
7049
return getDomainTypeInformation().getType();
7150
}
7251

52+
/**
53+
* Returns the {@link TypeInformation} of the id type of the repository.
54+
*
55+
* @return the {@link TypeInformation} class of the identifier of the entity managed by the repository. Will never be
56+
* {@literal null}.
57+
* @since 2.7
58+
*/
59+
TypeInformation<?> getIdTypeInformation();
60+
61+
/**
62+
* Returns the {@link TypeInformation}of the domain type the repository is declared to manage. Will never be
63+
* {@literal null}.
64+
*
65+
* @return the domain class the repository is handling.
66+
* @since 2.7
67+
*/
68+
TypeInformation<?> getDomainTypeInformation();
69+
70+
/**
71+
* Returns the repository interface.
72+
*
73+
* @return
74+
*/
75+
Class<?> getRepositoryInterface();
76+
7377
/**
7478
* Returns the type {@link Method} return type as it is declared in the repository. Considers suspended methods and
7579
* does not unwrap component types but leaves those for further inspection.

src/main/java/org/springframework/data/repository/core/support/AnnotationRepositoryMetadata.java

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.repository.core.support;
1717

18+
import java.util.function.Function;
19+
1820
import org.springframework.core.annotation.AnnotationUtils;
1921
import org.springframework.data.repository.RepositoryDefinition;
2022
import org.springframework.data.repository.core.RepositoryMetadata;
@@ -52,8 +54,8 @@ public AnnotationRepositoryMetadata(Class<?> repositoryInterface) {
5254
Assert.isTrue(AnnotationUtils.findAnnotation(repositoryInterface, RepositoryDefinition.class) != null,
5355
() -> String.format(NO_ANNOTATION_FOUND, repositoryInterface.getName()));
5456

55-
this.idType = resolveIdType(repositoryInterface);
56-
this.domainType = resolveDomainType(repositoryInterface);
57+
this.idType = resolveType(repositoryInterface, RepositoryDefinition::idClass);
58+
this.domainType = resolveType(repositoryInterface, RepositoryDefinition::domainClass);
5759
}
5860

5961
@Override
@@ -66,25 +68,15 @@ public TypeInformation<?> getDomainTypeInformation() {
6668
return this.domainType;
6769
}
6870

69-
private TypeInformation<?> resolveIdType(Class<?> repositoryInterface) {
70-
71-
RepositoryDefinition annotation = AnnotationUtils.findAnnotation(repositoryInterface, RepositoryDefinition.class);
72-
73-
if (annotation == null || annotation.idClass() == null) {
74-
throw new IllegalArgumentException(String.format("Could not resolve id type of %s!", repositoryInterface));
75-
}
76-
77-
return ClassTypeInformation.from(annotation.idClass());
78-
}
79-
80-
private TypeInformation<?> resolveDomainType(Class<?> repositoryInterface) {
71+
private static TypeInformation<?> resolveType(Class<?> repositoryInterface,
72+
Function<RepositoryDefinition, Class<?>> extractor) {
8173

8274
RepositoryDefinition annotation = AnnotationUtils.findAnnotation(repositoryInterface, RepositoryDefinition.class);
8375

84-
if (annotation == null || annotation.domainClass() == null) {
76+
if ((annotation == null) || (extractor.apply(annotation) == null)) {
8577
throw new IllegalArgumentException(String.format("Could not resolve domain type of %s!", repositoryInterface));
8678
}
8779

88-
return ClassTypeInformation.from(annotation.domainClass());
80+
return ClassTypeInformation.from(extractor.apply(annotation));
8981
}
9082
}

src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadata.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,28 @@ public DefaultRepositoryMetadata(Class<?> repositoryInterface) {
5656

5757
this.domainType = resolveTypeParameter(arguments, 0,
5858
() -> String.format("Could not resolve domain type of %s!", repositoryInterface));
59-
59+
6060
this.idType = resolveTypeParameter(arguments, 1,
6161
() -> String.format("Could not resolve id type of %s!", repositoryInterface));
6262
}
6363

64-
private static TypeInformation<?> resolveTypeParameter(List<TypeInformation<?>> arguments, int index,
65-
Supplier<String> exceptionMessage) {
66-
67-
if (arguments.size() <= index || arguments.get(index) == null) {
68-
throw new IllegalArgumentException(exceptionMessage.get());
69-
}
70-
71-
return arguments.get(index).getGenericTypeInformation();
72-
}
73-
64+
@Override
7465
public TypeInformation<?> getIdTypeInformation() {
7566
return this.idType;
7667
}
7768

69+
@Override
7870
public TypeInformation<?> getDomainTypeInformation() {
7971
return this.domainType;
8072
}
73+
74+
private static TypeInformation<?> resolveTypeParameter(List<TypeInformation<?>> arguments, int index,
75+
Supplier<String> exceptionMessage) {
76+
77+
if ((arguments.size() <= index) || (arguments.get(index) == null)) {
78+
throw new IllegalArgumentException(exceptionMessage.get());
79+
}
80+
81+
return arguments.get(index);
82+
}
8183
}

src/main/java/org/springframework/data/repository/core/support/MethodLookups.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ public RepositoryAwareMethodLookup(RepositoryMetadata repositoryMetadata) {
121121

122122
Assert.notNull(repositoryMetadata, "Repository metadata must not be null!");
123123

124-
this.entityType = ResolvableType.forType(repositoryMetadata.getDomainTypeInformation().getGenericType());
125-
this.idType = ResolvableType.forType(repositoryMetadata.getIdTypeInformation().getGenericType());
124+
this.entityType = repositoryMetadata.getDomainTypeInformation().toTypeDescriptor().getResolvableType();
125+
this.idType = repositoryMetadata.getIdTypeInformation().toTypeDescriptor().getResolvableType();
126126
this.repositoryInterface = repositoryMetadata.getRepositoryInterface();
127127
}
128128

src/main/java/org/springframework/data/repository/support/DomainClassConverter.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import org.springframework.context.ApplicationContext;
2323
import org.springframework.context.ApplicationContextAware;
24-
import org.springframework.core.ResolvableType;
2524
import org.springframework.core.convert.ConversionService;
2625
import org.springframework.core.convert.TypeDescriptor;
2726
import org.springframework.core.convert.converter.ConditionalGenericConverter;
@@ -30,7 +29,6 @@
3029
import org.springframework.data.repository.core.EntityInformation;
3130
import org.springframework.data.repository.core.RepositoryInformation;
3231
import org.springframework.data.util.Lazy;
33-
import org.springframework.data.util.TypeInformation;
3432
import org.springframework.lang.NonNull;
3533
import org.springframework.lang.Nullable;
3634
import org.springframework.util.Assert;
@@ -104,12 +102,6 @@ public void setApplicationContext(ApplicationContext context) {
104102
return repositories;
105103
});
106104
}
107-
108-
109-
private static TypeDescriptor getIdTypeDescriptor(RepositoryInformation information) {
110-
TypeInformation<?> idType = information.getIdTypeInformation();
111-
return new TypeDescriptor(ResolvableType.forType(idType.getGenericType()), null, idType.getType().getAnnotations());
112-
}
113105

114106
/**
115107
* Converter to create domain types from any source that can be converted into the domain types identifier type.
@@ -157,7 +149,7 @@ public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDe
157149
Class<?> domainType = targetType.getType();
158150
RepositoryInvoker invoker = repositoryInvokerFactory.getInvokerFor(domainType);
159151
RepositoryInformation information = repositories.getRequiredRepositoryInformation(domainType);
160-
TypeDescriptor idTypeDescriptor = getIdTypeDescriptor(information);
152+
TypeDescriptor idTypeDescriptor = information.getIdTypeInformation().toTypeDescriptor();
161153

162154
Object id = conversionService.convert(source, sourceType, idTypeDescriptor);
163155

@@ -181,7 +173,7 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
181173

182174
return repositoryInformation.map(it -> {
183175

184-
TypeDescriptor idTypeDescriptor = getIdTypeDescriptor(it);
176+
TypeDescriptor idTypeDescriptor = it.getIdTypeInformation().toTypeDescriptor();
185177

186178
return sourceType.equals(idTypeDescriptor)
187179
|| conversionService.canConvert(sourceType, idTypeDescriptor);
@@ -249,7 +241,7 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
249241

250242
return information.map(it -> {
251243

252-
TypeDescriptor idTypeDescriptor = getIdTypeDescriptor(it);
244+
TypeDescriptor idTypeDescriptor = it.getIdTypeInformation().toTypeDescriptor();
253245

254246
return targetType.equals(idTypeDescriptor)
255247
|| conversionService.canConvert(idTypeDescriptor, targetType);

src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.Optional;
2222

2323
import org.springframework.core.MethodParameter;
24-
import org.springframework.core.ResolvableType;
2524
import org.springframework.core.convert.ConversionException;
2625
import org.springframework.core.convert.ConversionService;
2726
import org.springframework.core.convert.TypeDescriptor;
@@ -74,7 +73,7 @@ public ReflectionRepositoryInvoker(Object repository, RepositoryMetadata metadat
7473
this.repository = repository;
7574
this.methods = metadata.getCrudMethods();
7675
TypeInformation<?> idType = metadata.getIdTypeInformation();
77-
this.idTypeDescriptor = new TypeDescriptor(ResolvableType.forType(idType.getGenericType()), null, idType.getType().getAnnotations());
76+
this.idTypeDescriptor = idType.toTypeDescriptor();
7877
this.conversionService = conversionService;
7978
}
8079

@@ -249,7 +248,7 @@ private <T> Optional<T> returnAsOptional(@Nullable Object source) {
249248
protected Object convertId(Object id) {
250249

251250
Assert.notNull(id, "Id must not be null!");
252-
TypeDescriptor idDescriptor = TypeDescriptor.forObject(id);
251+
TypeDescriptor idDescriptor = TypeDescriptor.forObject(id);
253252

254253
if (idDescriptor.isAssignableTo(idTypeDescriptor)) {
255254
return id;
@@ -259,7 +258,8 @@ protected Object convertId(Object id) {
259258

260259
if (result == null) {
261260
throw new IllegalStateException(
262-
String.format("Identifier conversion of %s to %s unexpectedly returned null!", id, idTypeDescriptor.getType()));
261+
String.format("Identifier conversion of %s to %s unexpectedly returned null!", id,
262+
idTypeDescriptor.getType()));
263263
}
264264

265265
return result;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Set;
2929

3030
import org.springframework.core.GenericTypeResolver;
31+
import org.springframework.core.convert.TypeDescriptor;
3132
import org.springframework.util.Assert;
3233
import org.springframework.util.ConcurrentReferenceHashMap;
3334
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
@@ -55,6 +56,7 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
5556
}
5657

5758
private final Class<S> type;
59+
private final Lazy<TypeDescriptor> descriptor;
5860

5961
/**
6062
* Simple factory method to easily create new instances of {@link ClassTypeInformation}.
@@ -89,8 +91,11 @@ public static <S> TypeInformation<S> fromReturnTypeOf(Method method) {
8991
* @param type
9092
*/
9193
ClassTypeInformation(Class<S> type) {
94+
9295
super(type, getTypeVariableMap(type));
96+
9397
this.type = type;
98+
this.descriptor = Lazy.of(() -> TypeDescriptor.valueOf(type));
9499
}
95100

96101
/**
@@ -152,6 +157,11 @@ public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
152157
return (TypeInformation<? extends S>) type;
153158
}
154159

160+
@Override
161+
public TypeDescriptor toTypeDescriptor() {
162+
return descriptor.get();
163+
}
164+
155165
@Override
156166
public String toString() {
157167
return type.getName();

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.lang.reflect.TypeVariable;
2020
import java.util.Map;
2121

22+
import org.springframework.core.ResolvableType;
23+
import org.springframework.core.convert.TypeDescriptor;
2224
import org.springframework.lang.Nullable;
2325

2426
/**
@@ -29,6 +31,7 @@
2931
public abstract class ParentTypeAwareTypeInformation<S> extends TypeDiscoverer<S> {
3032

3133
private final TypeDiscoverer<?> parent;
34+
private final Lazy<TypeDescriptor> descriptor;
3235
private int hashCode;
3336

3437
/**
@@ -44,7 +47,14 @@ protected ParentTypeAwareTypeInformation(Type type, TypeDiscoverer<?> parent) {
4447
protected ParentTypeAwareTypeInformation(Type type, TypeDiscoverer<?> parent, Map<TypeVariable<?>, Type> map) {
4548

4649
super(type, map);
50+
4751
this.parent = parent;
52+
this.descriptor = Lazy.of(() -> new TypeDescriptor(toResolvableType(), null, null));
53+
}
54+
55+
@Override
56+
public TypeDescriptor toTypeDescriptor() {
57+
return descriptor.get();
4858
}
4959

5060
@Override
@@ -57,6 +67,11 @@ protected TypeInformation<?> createInfo(Type fieldType) {
5767
return super.createInfo(fieldType);
5868
}
5969

70+
@Override
71+
protected ResolvableType toResolvableType() {
72+
return ResolvableType.forType(getType(), parent.toResolvableType());
73+
}
74+
6075
@Override
6176
public boolean equals(@Nullable Object obj) {
6277

@@ -80,7 +95,7 @@ public boolean equals(@Nullable Object obj) {
8095
public int hashCode() {
8196

8297
if (this.hashCode == 0) {
83-
this.hashCode = super.hashCode() + 31 * parent.hashCode();
98+
this.hashCode = super.hashCode() + (31 * parent.hashCode());
8499
}
85100

86101
return this.hashCode;

0 commit comments

Comments
 (0)