Polishing.

Move all primary interaction API into TypeInformation to prepare making all implementations package protected. Adapt all deprecated API usage in Spring Data Commons to the new one.

Benchmark results look as follows:

2.7
                                                                                                Mode  Cnt          Score          Error  Units
TypicalEntityReaderBenchmark.hasReadTarget                                                     thrpt   10   81982861,995 ±   269948,368  ops/s
TypicalEntityReaderBenchmark.kotlinDataClass                                                   thrpt   10   18472975,689 ±   161234,912  ops/s
TypicalEntityReaderBenchmark.kotlinDataClassWithDefaulting                                     thrpt   10    4029631,568 ±    18990,863  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorAndField                          thrpt   10   11495517,933 ±   209269,454  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorAndProperty                       thrpt   10   11192152,036 ±   249721,563  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorArgsCreation                      thrpt   10   18835101,279 ±    81895,338  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedFieldAccess                                  thrpt   10   16309896,939 ±    80376,406  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedPropertyAccess                               thrpt   10   15471691,537 ±    52426,121  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorAndField                         thrpt   10   12080159,283 ±    49922,326  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorAndProperty                      thrpt   10   12166160,066 ±   173876,051  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorArgsCreation                     thrpt   10   13548906,437 ±   485944,886  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveFieldAccess                                 thrpt   10   16457347,670 ±    64170,567  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectivePropertyAccess                              thrpt   10   14115878,948 ±    57491,268  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectivePropertyAccessWithCustomConversionRegistry  thrpt   10   15137904,257 ±    51148,726  ops/s

DefaultTypeMapperBenchmark.readTyped                                                           thrpt   10  146820080,682 ± 13254238,407  ops/s
DefaultTypeMapperBenchmark.readTypedWithBaseType                                               thrpt   10   55420351,827 ±  1888634,217  ops/s
DefaultTypeMapperBenchmark.readUntyped                                                         thrpt   10  193934847,273 ±   442867,276  ops/s
DefaultTypeMapperBenchmark.readUntypedWithBaseType                                             thrpt   10  192032977,366 ±  4300192,665  ops/s

3.0 – Before this change
                                                                                                Mode  Cnt          Score         Error  Units
TypicalEntityReaderBenchmark.hasReadTarget                                                     thrpt   10   81124249,138 ±  174095,495  ops/s
TypicalEntityReaderBenchmark.kotlinDataClass                                                   thrpt   10   18685936,220 ±  243939,063  ops/s
TypicalEntityReaderBenchmark.kotlinDataClassWithDefaulting                                     thrpt   10    4125709,048 ±   17615,890  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorAndField                          thrpt   10   12277837,989 ±   96973,908  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorAndProperty                       thrpt   10   12419978,391 ±   45780,658  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorArgsCreation                      thrpt   10   18744593,010 ±   71655,735  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedFieldAccess                                  thrpt   10   16497286,465 ±   41150,718  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedPropertyAccess                               thrpt   10   15639640,554 ±   69758,391  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorAndField                         thrpt   10   11715068,503 ±   66085,900  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorAndProperty                      thrpt   10   12159285,083 ±   58171,950  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorArgsCreation                     thrpt   10   13711647,512 ±   66496,271  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveFieldAccess                                 thrpt   10   16287057,838 ±   54826,532  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectivePropertyAccess                              thrpt   10   13847665,634 ±  127834,292  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectivePropertyAccessWithCustomConversionRegistry  thrpt   10   15099300,270 ±   89030,403  ops/s

DefaultTypeMapperBenchmark.readTyped                                                           thrpt   10  151893972,774 ±  494555,250  ops/s
DefaultTypeMapperBenchmark.readTypedWithBaseType                                               thrpt   10   55869432,892 ±  389099,409  ops/s
DefaultTypeMapperBenchmark.readUntyped                                                         thrpt   10  192661151,755 ± 1075079,924  ops/s
DefaultTypeMapperBenchmark.readUntypedWithBaseType                                             thrpt   10  192315554,269 ± 3061160,314  ops/s

3.0 – After this change
                                                                                                Mode  Cnt          Score         Error  Units
TypicalEntityReaderBenchmark.hasReadTarget                                                     thrpt   10   82650245,236 ±  321384,577  ops/s
TypicalEntityReaderBenchmark.kotlinDataClass                                                   thrpt   10   21304680,430 ±  396764,834  ops/s
TypicalEntityReaderBenchmark.kotlinDataClassWithDefaulting                                     thrpt   10    4422187,275 ±   35088,721  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorAndField                          thrpt   10   13345503,496 ±  180490,423  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorAndProperty                       thrpt   10   13911653,366 ±  266028,886  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedConstructorArgsCreation                      thrpt   10   21192316,261 ±  356534,938  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedFieldAccess                                  thrpt   10   17533419,173 ±   49226,408  ops/s
TypicalEntityReaderBenchmark.simpleEntityGeneratedPropertyAccess                               thrpt   10   17393382,078 ±  114774,442  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorAndField                         thrpt   10   12867285,952 ±   62870,980  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorAndProperty                      thrpt   10   13208453,130 ±  272795,070  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveConstructorArgsCreation                     thrpt   10   14998467,714 ±   84265,782  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectiveFieldAccess                                 thrpt   10   17365982,413 ±  115287,403  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectivePropertyAccess                              thrpt   10   15503841,949 ±  123631,592  ops/s
TypicalEntityReaderBenchmark.simpleEntityReflectivePropertyAccessWithCustomConversionRegistry  thrpt   10   15716685,449 ±   64686,646  ops/s

DefaultTypeMapperBenchmark.readTyped                                                           thrpt   10  154433835,941 ± 1391455,835  ops/s
DefaultTypeMapperBenchmark.readTypedWithBaseType                                               thrpt   10   77919886,633 ±  438244,738  ops/s
DefaultTypeMapperBenchmark.readUntyped                                                         thrpt   10  194435210,217 ±  936274,571  ops/s
DefaultTypeMapperBenchmark.readUntypedWithBaseType                                             thrpt   10  193768718,043 ±  475103,167  ops/s

Original pull request: #2572.
Related ticket: #2312.
This commit is contained in:
Oliver Drotbohm
2022-04-06 14:46:18 +02:00
parent 1d6331ccb6
commit 07342cffb5
60 changed files with 655 additions and 678 deletions

View File

@@ -16,45 +16,85 @@
package org.springframework.data.util;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
import org.springframework.util.ConcurrentLruCache;
/**
* {@link TypeInformation} for a plain {@link Class}.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @deprecated since 3.0 to go package protected at some point. Refer to {@link TypeInformation} only.
*/
@Deprecated
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
public static final ClassTypeInformation<Collection> COLLECTION = new ClassTypeInformation(Collection.class);
public static final ClassTypeInformation<List> LIST = new ClassTypeInformation(List.class);
public static final ClassTypeInformation<Set> SET = new ClassTypeInformation(Set.class);
public static final ClassTypeInformation<Map> MAP = new ClassTypeInformation(Map.class);
public static final ClassTypeInformation<Object> OBJECT = new ClassTypeInformation(Object.class);
private static final ConcurrentLruCache<ResolvableType, ClassTypeInformation<?>> cache = new ConcurrentLruCache<>(64,
ClassTypeInformation::new);
private static final Map<Class<?>, ClassTypeInformation<?>> cache = new ConcurrentReferenceHashMap<>(64,
ReferenceType.WEAK);
@Deprecated public static final ClassTypeInformation<Collection> COLLECTION;
@Deprecated public static final ClassTypeInformation<List> LIST;
@Deprecated public static final ClassTypeInformation<Set> SET;
@Deprecated public static final ClassTypeInformation<Map> MAP;
@Deprecated public static final ClassTypeInformation<Object> OBJECT;
static {
OBJECT = (ClassTypeInformation<Object>) cache.get(ResolvableType.forClass(Object.class));
COLLECTION = (ClassTypeInformation<Collection>) cache.get(ResolvableType.forClass(Collection.class));
LIST = (ClassTypeInformation<List>) cache.get(ResolvableType.forClass(List.class));
SET = (ClassTypeInformation<Set>) cache.get(ResolvableType.forClass(Set.class));
MAP = (ClassTypeInformation<Map>) cache.get(ResolvableType.forClass(Map.class));
}
private final Class<S> type;
ClassTypeInformation(ResolvableType type) {
super(type);
this.type = (Class<S>) type.resolve(Object.class);
}
private ClassTypeInformation(Class<S> type) {
super(ResolvableType.forClass(type));
this.type = type;
}
/**
* @param <S>
* @param type
* @return
* @deprecated since 3.0. Use {@link TypeInformation#of} instead.
*/
@Deprecated
public static <S> ClassTypeInformation<S> from(Class<S> type) {
return cti(ResolvableType.forClass(type));
}
static <S> ClassTypeInformation<S> cti(ResolvableType type) {
Assert.notNull(type, "Type must not be null");
return (ClassTypeInformation<S>) cache.get(type);
}
/**
* Warning: Does not fully resolve generic arguments.
*
* @param method
* @return
* @deprecated since 3.0 Use {@link #fromReturnTypeOf(Method, Class)} instead.
* @deprecated since 3.0. Use {@link TypeInformation#fromReturnTypeOf(Method)} instead.
*/
@Deprecated
public static TypeInformation<?> fromReturnTypeOf(Method method) {
return new TypeDiscoverer<>(ResolvableType.forMethodReturnType(method));
public static <S> TypeInformation<S> fromReturnTypeOf(Method method) {
return (TypeInformation<S>) TypeInformation.of(ResolvableType.forMethodReturnType(method));
}
/**
@@ -62,35 +102,18 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
* @param actualType can be {@literal null}.
* @return
*/
public static TypeInformation<?> fromReturnTypeOf(Method method, @Nullable Class<?> actualType) {
static TypeInformation<?> fromReturnTypeOf(Method method, @Nullable Class<?> actualType) {
if(actualType == null) {
return new TypeDiscoverer<>(ResolvableType.forMethodReturnType(method));
}
return new TypeDiscoverer<>(ResolvableType.forMethodReturnType(method, actualType));
}
var type = actualType == null
? ResolvableType.forMethodReturnType(method)
: ResolvableType.forMethodReturnType(method, actualType);
Class<?> type;
static {
Arrays.asList(COLLECTION, LIST, SET, MAP, OBJECT).forEach(it -> cache.put(it.getType(), it));
}
public static <S> ClassTypeInformation<S> from(Class<S> type) {
Assert.notNull(type, "Type must not be null");
return (ClassTypeInformation<S>) cache.computeIfAbsent(type, ClassTypeInformation::new);
}
ClassTypeInformation(Class<S> type) {
super(ResolvableType.forClass(type));
this.type = type;
return TypeInformation.of(type);
}
@Override
public Class<S> getType() {
return (Class<S>) type;
return type;
}
@Override
@@ -104,7 +127,7 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
}
@Override
public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
public TypeInformation<? extends S> specialize(TypeInformation<?> type) {
return (TypeInformation<? extends S>) type;
}
@@ -112,9 +135,4 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
public String toString() {
return type.getName();
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
}

View File

@@ -22,11 +22,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -37,6 +35,7 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentLruCache;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
@@ -49,55 +48,36 @@ import org.springframework.util.ReflectionUtils;
* @author Jürgen Diez
* @author Alessandro Nistico
* @author Johannes Englmeier
* @deprecated since 3.0 to go package protected at some point. Prefer to refer to {@link TypeInformation} instead.
*/
public class TypeDiscoverer<S> implements TypeInformation<S> {
@Deprecated
class TypeDiscoverer<S> implements TypeInformation<S> {
protected static final Class<?>[] MAP_TYPES;
private static final Class<?>[] COLLECTION_TYPES;
static {
var classLoader = TypeDiscoverer.class.getClassLoader();
Set<Class<?>> mapTypes = new HashSet<>();
mapTypes.add(Map.class);
try {
mapTypes.add(ClassUtils.forName("io.vavr.collection.Map", classLoader));
} catch (ClassNotFoundException o_O) {}
MAP_TYPES = mapTypes.toArray(new Class[0]);
Set<Class<?>> collectionTypes = new HashSet<>();
collectionTypes.add(Collection.class);
try {
collectionTypes.add(ClassUtils.forName("io.vavr.collection.Seq", classLoader));
} catch (ClassNotFoundException o_O) {}
try {
collectionTypes.add(ClassUtils.forName("io.vavr.collection.Set", classLoader));
} catch (ClassNotFoundException o_O) {}
COLLECTION_TYPES = collectionTypes.toArray(new Class[0]);
}
ResolvableType resolvableType;
private Map<String, Optional<TypeInformation<?>>> fields = new ConcurrentHashMap<>();
private static final ConcurrentLruCache<ResolvableType, TypeInformation<?>> CACHE = new ConcurrentLruCache<>(64,
TypeDiscoverer::new);
private final ResolvableType resolvableType;
private final Map<String, Optional<TypeInformation<?>>> fields = new ConcurrentHashMap<>();
private final Lazy<TypeInformation<?>> componentType;
private final Lazy<TypeInformation<?>> valueType;
private final Map<Constructor<?>, List<TypeInformation<?>>> constructorParameters = new ConcurrentHashMap<>();
private final Lazy<List<TypeInformation<?>>> typeArguments;
public TypeDiscoverer(Class<?> type) {
this(ResolvableType.forClass(type));
}
public TypeDiscoverer(ResolvableType type) {
protected TypeDiscoverer(ResolvableType type) {
Assert.notNull(type, "Type must not be null");
this.resolvableType = type;
this.componentType = Lazy.of(this::doGetComponentType);
this.valueType = Lazy.of(this::doGetMapValueType);
this.typeArguments = Lazy.of(this::doGetTypeArguments);
}
static TypeDiscoverer<?> td(ResolvableType type) {
Assert.notNull(type, "Type must not be null");
return (TypeDiscoverer<?>) CACHE.get(type);
}
@Override
@@ -105,16 +85,16 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
Assert.notNull(constructor, "Constructor must not be null");
List<TypeInformation<?>> target = new ArrayList<>();
for (int i = 0; i < constructor.getParameterCount(); i++) {
target.add(new TypeDiscoverer<>(ResolvableType.forConstructorParameter(constructor, i)));
}
return target;
}
return constructorParameters.computeIfAbsent(constructor, it -> {
@Override
public TypeDescriptor toTypeDescriptor() {
return new TypeDescriptor(resolvableType, null, null);
List<TypeInformation<?>> target = new ArrayList<>();
for (int i = 0; i < it.getParameterCount(); i++) {
target.add(TypeInformation.of(ResolvableType.forConstructorParameter(it, i)));
}
return target;
});
}
@Nullable
@@ -137,56 +117,11 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
return info.getProperty(name.substring(separatorIndex + 1));
}
private Optional<TypeInformation<?>> getPropertyInformation(String fieldname) {
Class<?> rawType = resolvableType.toClass();
var field = ReflectionUtils.findField(rawType, fieldname);
if (field != null) {
return Optional.of(new TypeDiscoverer(ResolvableType.forField(field, resolvableType)));
}
return findPropertyDescriptor(rawType, fieldname).map(it -> {
if (it.getReadMethod() != null) {
return new TypeDiscoverer(ResolvableType.forMethodReturnType(it.getReadMethod(), rawType));
}
if (it.getWriteMethod() != null) {
return new TypeDiscoverer(ResolvableType.forMethodParameter(it.getWriteMethod(), 0, rawType));
}
return new TypeDiscoverer(ResolvableType.forType(it.getPropertyType(), resolvableType));
});
}
private Optional<PropertyDescriptor> findPropertyDescriptor(Class<?> type, String fieldname) {
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(type, fieldname);
if (descriptor != null) {
return Optional.of(descriptor);
}
List<Class<?>> superTypes = new ArrayList<>();
superTypes.addAll(Arrays.asList(type.getInterfaces()));
superTypes.add(type.getSuperclass());
return Streamable.of(type.getInterfaces()).stream()//
.flatMap(it -> Optionals.toStream(findPropertyDescriptor(it, fieldname)))//
.findFirst();
}
@Override
public boolean isCollectionLike() {
Class<S> type = getType();
for (Class<?> collectionType : COLLECTION_TYPES) {
if (collectionType.isAssignableFrom(type)) {
return true;
}
}
return type.isArray() //
|| Iterable.class.equals(type) //
|| Collection.class.isAssignableFrom(type) //
@@ -203,66 +138,27 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
@Nullable
protected TypeInformation<?> doGetComponentType() {
var rawType = getType();
if (rawType.isArray()) {
return new TypeDiscoverer<>(resolvableType.getComponentType());
if (resolvableType.isArray()) {
return TypeInformation.of(resolvableType.getComponentType());
}
if (isMap()) {
if (ClassUtils.isAssignable(Map.class, rawType)) {
ResolvableType mapValueType = resolvableType.asMap().getGeneric(0);
if (ResolvableType.NONE.equals(mapValueType)) {
return null;
}
Class<S> rawType = getType();
return mapValueType != null ? new TypeDiscoverer(mapValueType) : new ClassTypeInformation<>(Object.class);
}
if (resolvableType.hasGenerics()) {
ResolvableType mapValueType = resolvableType.getGeneric(0);
return mapValueType != null ? new TypeDiscoverer(mapValueType) : new ClassTypeInformation<>(Object.class);
}
return Arrays.stream(resolvableType.getInterfaces()).filter(ResolvableType::hasGenerics)
.findFirst()
.map(it -> it.getGeneric(0))
.map(TypeDiscoverer::new)
.orElse(null);
if (isMap()) {
return getTypeArgument(CustomCollections.getMapBaseType(rawType), 0);
}
if (Iterable.class.isAssignableFrom(rawType)) {
ResolvableType iterableType = resolvableType.as(Iterable.class);
ResolvableType mapValueType = iterableType.getGeneric(0);
if (ResolvableType.NONE.equals(mapValueType)) {
return null;
}
if (resolvableType.hasGenerics()) {
mapValueType = resolvableType.getGeneric(0);
return mapValueType != null ? new TypeDiscoverer(mapValueType) : new ClassTypeInformation<>(Object.class);
}
return mapValueType.resolve() != null ? new TypeDiscoverer<>(mapValueType) : null;
return getTypeArgument(Iterable.class, 0);
}
if (isNullableWrapper()) {
ResolvableType mapValueType = resolvableType.getGeneric(0);
if (ResolvableType.NONE.equals(mapValueType)) {
return null;
}
return mapValueType != null ? new TypeDiscoverer(mapValueType) : new ClassTypeInformation<>(Object.class);
return getTypeArgument(rawType, 0);
}
if (resolvableType.hasGenerics()) {
ResolvableType mapValueType = resolvableType.getGeneric(0);
return mapValueType != null ? new TypeDiscoverer(mapValueType) : new ClassTypeInformation<>(Object.class);
}
List<TypeInformation<?>> arguments = getTypeArguments();
return null;
}
private boolean isNullableWrapper() {
return NullableWrapperConverters.supports(getType());
return arguments.size() > 0 ? arguments.get(0) : null;
}
@Override
@@ -279,50 +175,31 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
@Nullable
protected TypeInformation<?> doGetMapValueType() {
if (isMap()) {
if (ClassUtils.isAssignable(Map.class, getType())) {
ResolvableType mapValueType = resolvableType.asMap().getGeneric(1);
if (ResolvableType.NONE.equals(mapValueType)) {
return null;
}
return mapValueType != null ? new TypeDiscoverer(mapValueType) : new ClassTypeInformation<>(Object.class);
}
if (resolvableType.hasGenerics()) {
ResolvableType mapValueType = resolvableType.getGeneric(1);
return mapValueType != null ? new TypeDiscoverer(mapValueType) : new ClassTypeInformation<>(Object.class);
}
return Arrays.stream(resolvableType.getInterfaces()).filter(ResolvableType::hasGenerics)
.findFirst()
.map(it -> it.getGeneric(1))
.map(TypeDiscoverer::new)
.orElse(null);
}
if (!resolvableType.hasGenerics()) {
return null;
}
ResolvableType x = Arrays.stream(resolvableType.getGenerics()).skip(1).findFirst().orElse(null);
if ((x == null) || ResolvableType.NONE.equals(x)) {
return null;
}
return new TypeDiscoverer<>(x);
return isMap() //
? getTypeArgument(CustomCollections.getMapBaseType(getType()), 1)
: getTypeArguments().stream().skip(1).findFirst().orElse(null);
}
@Override
@SuppressWarnings("unchecked")
public Class<S> getType() {
return (Class<S>) resolvableType.toClass();
}
@Override
public TypeDescriptor toTypeDescriptor() {
return new TypeDescriptor(resolvableType, getType(), null);
}
@Override
public ClassTypeInformation<?> getRawTypeInformation() {
return new ClassTypeInformation<>(this.resolvableType.getRawClass());
return new ClassTypeInformation<>(ResolvableType.forRawClass(resolvableType.getRawClass()));
}
@Nullable
@Override
public TypeInformation<?> getActualType() {
if (isMap()) {
return getMapValueType();
}
@@ -331,8 +208,6 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
return getComponentType();
}
// TODO: Consider that we will support value types beyond Optional<T>, such as Json<T>, Foo<T> that should remain
// configurable.
if (isNullableWrapper()) {
return getComponentType();
}
@@ -342,10 +217,7 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
@Override
public TypeInformation<?> getReturnType(Method method) {
Assert.notNull(method, "Method must not be null");
return new TypeDiscoverer(ResolvableType.forMethodReturnType(method, getType()));
return TypeInformation.of(ResolvableType.forMethodReturnType(method, getType()));
}
@Override
@@ -353,9 +225,11 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
Assert.notNull(method, "Method most not be null");
return Streamable.of(method.getParameters()).stream().map(MethodParameter::forParameter)
.map(it -> ResolvableType.forMethodParameter(it, resolvableType)).map(TypeDiscoverer::new)
.collect(Collectors.toList());
return Arrays.stream(method.getParameters()) //
.map(MethodParameter::forParameter) //
.map(it -> ResolvableType.forMethodParameter(it, resolvableType)) //
.<TypeInformation<?>> map(TypeInformation::of) //
.toList();
}
@@ -373,46 +247,24 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
return this;
}
List<ResolvableType> candidates = new ArrayList<>();
var resolvableSuperType = resolvableType.as(superType);
var type = resolvableType.getType();
ResolvableType genericSuperclass = resolvableType.getSuperType();
if ((genericSuperclass != null) && !genericSuperclass.equals(ResolvableType.NONE)) {
candidates.add(genericSuperclass);
if (!(type instanceof Class) || !ObjectUtils.isEmpty(((Class<?>) type).getTypeParameters())) {
return TypeInformation.of(resolvableSuperType);
}
candidates.addAll(Arrays.asList(resolvableType.getInterfaces()));
var noGenericsResolvable = !Arrays.stream(resolvableSuperType.resolveGenerics())
.filter(it -> it != null)
.findAny()
.isPresent();
for (var candidate : candidates) {
if (ObjectUtils.nullSafeEquals(superType, candidate.toClass())) {
if (resolvableType.getType() instanceof Class) {
if (ObjectUtils.isEmpty(((Class) resolvableType.getType()).getTypeParameters())) {
Class<?>[] classes = candidate.resolveGenerics(null);
if (!Arrays.stream(classes).filter(it -> it != null).findAny().isPresent()) {
return new TypeDiscoverer<>(ResolvableType.forRawClass(superType));
}
}
}
return new TypeDiscoverer(ResolvableType.forClass(superType, getType()));
} else {
var sup = candidate.getSuperType();
if ((sup != null) && !ResolvableType.NONE.equals(sup)) {
if (sup.equals(resolvableType)) {
return this;
}
return new TypeDiscoverer(sup);
}
}
}
return new TypeDiscoverer(resolvableType.as(superType));
return noGenericsResolvable
? new ClassTypeInformation<>(ResolvableType.forRawClass(superType))
: TypeInformation.of(resolvableSuperType);
}
/* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#isAssignableFrom(org.springframework.data.util.TypeInformation)
*/
@Override
public boolean isAssignableFrom(TypeInformation<?> target) {
TypeInformation<?> superTypeInformation = target.getSuperTypeInformation(getType());
@@ -420,6 +272,7 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
if (superTypeInformation == null) {
return false;
}
if (superTypeInformation.equals(this)) {
return true;
}
@@ -433,62 +286,58 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
@Override
public List<TypeInformation<?>> getTypeArguments() {
return typeArguments.get();
}
private List<TypeInformation<?>> doGetTypeArguments() {
if (!resolvableType.hasGenerics()) {
return Collections.emptyList();
}
return Arrays.stream(resolvableType.getGenerics()).map(it -> {
if ((it == null) || ResolvableType.NONE.equals(it)) {
return null;
}
return new TypeDiscoverer<>(it);
}).collect(Collectors.toList());
return Arrays.stream(resolvableType.getGenerics())
.<TypeInformation<?>> map(it -> it.resolve(Object.class) == null ? null : TypeInformation.of(it))
.toList();
}
@Override
public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
// if(isAssignableFrom(type)) {
// return new ClassTypeInformation(type.getType());
// }
// return new NewTypeDiscoverer(type.resolvableType.as(getType()));
// if(type.resolvableType.isAssignableFrom(type.resolvableType)) {
// return (TypeInformation<? extends S>) type;
// }
@SuppressWarnings("unchecked")
public TypeInformation<? extends S> specialize(TypeInformation<?> type) {
if (this.resolvableType.getGenerics().length == type.resolvableType.getGenerics().length) {
return new TypeDiscoverer<>(
if (this.getTypeArguments().size() == type.getTypeArguments().size()) {
return (TypeInformation<? extends S>) TypeInformation.of(
ResolvableType.forClassWithGenerics(type.getType(), this.resolvableType.getGenerics()));
}
return new ClassTypeInformation(type.getType());
return TypeInformation.of((Class<S>) type.getType());
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if ((o == null) || !ClassUtils.isAssignable(getClass(), o.getClass())) {
return false;
}
TypeDiscoverer<?> that = (TypeDiscoverer<?>) o;
var that = (TypeDiscoverer<?>) o;
if (!ObjectUtils.nullSafeEquals(getType(), that.getType())) {
return false;
}
List<? extends Class<?>> collect1 = Arrays.stream(resolvableType.getGenerics()).map(ResolvableType::toClass)
.collect(Collectors.toList());
List<? extends Class<?>> collect2 = Arrays.stream(that.resolvableType.getGenerics()).map(ResolvableType::toClass)
var collect1 = Arrays.stream(resolvableType.getGenerics()) //
.map(ResolvableType::toClass) //
.collect(Collectors.toList());
if (!ObjectUtils.nullSafeEquals(collect1, collect2)) {
return false;
}
return true;
var collect2 = Arrays.stream(that.resolvableType.getGenerics()) //
.map(ResolvableType::toClass) //
.collect(Collectors.toList());
return ObjectUtils.nullSafeEquals(collect1, collect2);
}
@Override
@@ -498,6 +347,57 @@ public class TypeDiscoverer<S> implements TypeInformation<S> {
@Override
public String toString() {
return getType().getName();
return resolvableType.toString();
}
@Nullable
private TypeInformation<?> getTypeArgument(Class<?> bound, int index) {
var superTypeInformation = getSuperTypeInformation(bound);
if (superTypeInformation == null) {
return null;
}
var arguments = superTypeInformation.getTypeArguments();
if (arguments.isEmpty() || (index > (arguments.size() - 1))) {
return null;
}
return arguments.get(index);
}
private Optional<TypeInformation<?>> getPropertyInformation(String fieldname) {
var rawType = getType();
var field = ReflectionUtils.findField(rawType, fieldname);
return field != null
? Optional.of(TypeInformation.of(ResolvableType.forField(field, resolvableType)))
: Optional.ofNullable(BeanUtils.getPropertyDescriptor(rawType, fieldname))
.map(it -> from(it, rawType))
.map(TypeInformation::of);
}
private ResolvableType from(PropertyDescriptor descriptor, Class<?> rawType) {
var method = descriptor.getReadMethod();
if (method != null) {
return ResolvableType.forMethodReturnType(method, rawType);
}
method = descriptor.getWriteMethod();
if (method != null) {
return ResolvableType.forMethodParameter(method, 0, rawType);
}
return ResolvableType.forType(descriptor.getPropertyType(), resolvableType);
}
private boolean isNullableWrapper() {
return NullableWrapperConverters.supports(getType());
}
}

View File

@@ -17,10 +17,15 @@ package org.springframework.data.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Interface to access property types and resolving generics on the way. Starting with a {@link ClassTypeInformation}
@@ -30,9 +35,77 @@ import org.springframework.lang.Nullable;
* @author Mark Paluch
* @author Alessandro Nistico
* @author Johannes Englmeier
* @author Christoph Strobl
*/
@SuppressWarnings({ "deprecation", "rawtypes" })
public interface TypeInformation<S> {
public static final TypeInformation<Collection> COLLECTION = ClassTypeInformation.COLLECTION;
public static final TypeInformation<List> LIST = ClassTypeInformation.LIST;
public static final TypeInformation<Set> SET = ClassTypeInformation.SET;
public static final TypeInformation<Map> MAP = ClassTypeInformation.MAP;
public static final TypeInformation<Object> OBJECT = ClassTypeInformation.OBJECT;
static TypeInformation<?> orObject(@Nullable ResolvableType type) {
return type == null ? ClassTypeInformation.OBJECT : of(type);
}
/**
* Creates a new {@link TypeInformation} from the given {@link ResolvableType}.
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
* @since 3.0
*/
public static TypeInformation<?> of(ResolvableType type) {
Assert.notNull(type, "Type must not be null");
return type.hasGenerics() || (type.isArray() && type.getComponentType().hasGenerics()) //
? TypeDiscoverer.td(type)
: ClassTypeInformation.cti(type);
}
/**
* Creates a new {@link TypeInformation} for the given {@link Class}.
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
* @since 3.0
*/
public static <S> TypeInformation<S> of(Class<S> type) {
Assert.notNull(type, "Type must not be null");
return ClassTypeInformation.from(type);
}
/**
* Returns a {@link TypeInformation} for the given {@link Method}.
*
* @param method must not be {@literal null}.
* @return will never be {@literal null}.
* @since 3.0
*/
public static TypeInformation<?> fromReturnTypeOf(Method method) {
Assert.notNull(method, "Method must not be null");
return fromReturnTypeOf(method, null);
}
/**
* Returns a {@link TypeInformation} for the given method as declared on the given type.
*
* @param method must not be {@literal null}.
* @param type can be {@literal null}.
* @return will never be {@literal null}.
* @since 3.0
*/
public static TypeInformation<?> fromReturnTypeOf(Method method, @Nullable Class<?> type) {
return ClassTypeInformation.fromReturnTypeOf(method, type);
}
/**
* Returns the {@link TypeInformation}s for the parameters of the given {@link Constructor}.
*
@@ -162,7 +235,7 @@ public interface TypeInformation<S> {
default TypeInformation<?> getUserTypeInformation() {
Class<?> userType = ProxyUtils.getUserClass(getType());
return userType.equals(getType()) ? this : ClassTypeInformation.from(userType);
return userType.equals(getType()) ? this : TypeInformation.of(userType);
}
/**
@@ -276,12 +349,27 @@ public interface TypeInformation<S> {
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
* @deprecated since 3.0. Use {@link #specialize(TypeInformation)} instead, i.e. switch the given parameter's type to
* {@link TypeInformation} in the first place.
*/
TypeInformation<? extends S> specialize(ClassTypeInformation<?> type);
default TypeInformation<? extends S> specialize(TypeInformation<?> type) {
return specialize(ClassTypeInformation.from(type.getType()));
@Deprecated
default TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
return specialize((TypeInformation<?>) type);
}
/**
* Specializes the given (raw) {@link TypeInformation} using the context of the current potentially parameterized
* type, basically turning the given raw type into a parameterized one. Will return the given type as is if no
* generics are involved.
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
*/
@SuppressWarnings("unchecked")
default TypeInformation<? extends S> specialize(TypeInformation<?> type) {
return (TypeInformation<? extends S>) type;
}
/**
* Returns whether the current type is a sub type of the given one, i.e. whether it's assignable but not the same one.
*