提交 08517667 编写于 作者: J Juergen Hoeller

Accept non-generic type match as a fallback

DefaultListableBeanFactory performs a fallback check for autowire candidates now, which GenericTypeAwareAutowireCandidateResolver implements to accept raw type matches if the target class has unresolvable type variables. Full generic matches are still preferred; the BeanFactory will only start looking for fallback matches if the first pass led to an empty result.

Issue: SPR-10993
Issue: SPR-11004
上级 02f9b713
...@@ -212,6 +212,29 @@ public class DependencyDescriptor implements Serializable { ...@@ -212,6 +212,29 @@ public class DependencyDescriptor implements Serializable {
ResolvableType.forMethodParameter(this.methodParameter)); ResolvableType.forMethodParameter(this.methodParameter));
} }
/**
* Return whether a fallback match is allowed.
* <p>This is {@code false} by default but may be overridden to return {@code true} in order
* to suggest to a {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
* that a fallback match is acceptable as well.
*/
public boolean fallbackMatchAllowed() {
return false;
}
/**
* Return a variant of this descriptor that is intended for a fallback match.
* @see #fallbackMatchAllowed()
*/
public DependencyDescriptor forFallbackMatch() {
return new DependencyDescriptor(this) {
@Override
public boolean fallbackMatchAllowed() {
return true;
}
};
}
/** /**
* Initialize parameter name discovery for the underlying method parameter, if any. * Initialize parameter name discovery for the underlying method parameter, if any.
* <p>This method does not actually try to retrieve the parameter name at * <p>This method does not actually try to retrieve the parameter name at
...@@ -241,7 +264,8 @@ public class DependencyDescriptor implements Serializable { ...@@ -241,7 +264,8 @@ public class DependencyDescriptor implements Serializable {
if (this.nestingLevel > 1) { if (this.nestingLevel > 1) {
Type type = this.field.getGenericType(); Type type = this.field.getGenericType();
if (type instanceof ParameterizedType) { if (type instanceof ParameterizedType) {
Type arg = ((ParameterizedType) type).getActualTypeArguments()[0]; Type[] args = ((ParameterizedType) type).getActualTypeArguments();
Type arg = args[args.length - 1];
if (arg instanceof Class) { if (arg instanceof Class) {
return (Class) arg; return (Class) arg;
} }
......
...@@ -979,6 +979,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto ...@@ -979,6 +979,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
result.put(candidateName, getBean(candidateName)); result.put(candidateName, getBean(candidateName));
} }
} }
if (result.isEmpty()) {
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidateName : candidateNames) {
if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
result.put(candidateName, getBean(candidateName));
}
}
}
return result; return result;
} }
......
...@@ -57,24 +57,28 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid ...@@ -57,24 +57,28 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
// if explicitly false, do not proceed with any other checks // if explicitly false, do not proceed with any other checks
return false; return false;
} }
return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor.getResolvableType())); return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor));
} }
/** /**
* Match the given dependency type with its generic type information * Match the given dependency type with its generic type information
* against the given candidate bean definition. * against the given candidate bean definition.
*/ */
protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, ResolvableType dependencyType) { protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (dependencyType.getType() instanceof Class) { ResolvableType dependencyType = descriptor.getResolvableType();
if (!dependencyType.hasGenerics()) {
// No generic type -> we know it's a Class type-match, so no need to check again. // No generic type -> we know it's a Class type-match, so no need to check again.
return true; return true;
} }
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
ResolvableType targetType = null; ResolvableType targetType = null;
if (bd.getResolvedFactoryMethod() != null) { RootBeanDefinition rbd = null;
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
}
if (rbd != null && rbd.getResolvedFactoryMethod() != null) {
// Should typically be set for any kind of factory method, since the BeanFactory // Should typically be set for any kind of factory method, since the BeanFactory
// pre-resolves them before reaching out to the AutowireCandidateResolver... // pre-resolves them before reaching out to the AutowireCandidateResolver...
targetType = ResolvableType.forMethodReturnType(bd.getResolvedFactoryMethod()); targetType = ResolvableType.forMethodReturnType(rbd.getResolvedFactoryMethod());
} }
if (targetType == null) { if (targetType == null) {
// Regular case: straight bean instance, with BeanFactory available. // Regular case: straight bean instance, with BeanFactory available.
...@@ -86,14 +90,20 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid ...@@ -86,14 +90,20 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
} }
// Fallback: no BeanFactory set, or no type resolvable through it // Fallback: no BeanFactory set, or no type resolvable through it
// -> best-effort match against the target class if applicable. // -> best-effort match against the target class if applicable.
if (targetType == null && bd.hasBeanClass() && bd.getFactoryMethodName() == null) { if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
Class<?> beanClass = bd.getBeanClass(); Class<?> beanClass = rbd.getBeanClass();
if (!FactoryBean.class.isAssignableFrom(beanClass)) { if (!FactoryBean.class.isAssignableFrom(beanClass)) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass)); targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
} }
} }
} }
return (targetType == null || dependencyType.isAssignableFrom(targetType)); if (targetType == null) {
return true;
}
if (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics()) {
return descriptor.getDependencyType().isAssignableFrom(targetType.getRawClass());
}
return dependencyType.isAssignableFrom(targetType);
} }
......
...@@ -1305,6 +1305,130 @@ public class AutowiredAnnotationBeanPostProcessorTests { ...@@ -1305,6 +1305,130 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(ir, bean.integerRepositoryMap.get("integerRepo")); assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
} }
@Test
public void testGenericsBasedConstructorInjectionWithNonTypedTarget() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
GenericRepository gr = new GenericRepository();
bf.registerSingleton("genericRepo", gr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
assertSame(gr, bean.stringRepository);
assertSame(gr, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length);
assertSame(1, bean.integerRepositoryArray.length);
assertSame(gr, bean.stringRepositoryArray[0]);
assertSame(gr, bean.integerRepositoryArray[0]);
assertSame(1, bean.stringRepositoryList.size());
assertSame(1, bean.integerRepositoryList.size());
assertSame(gr, bean.stringRepositoryList.get(0));
assertSame(gr, bean.integerRepositoryList.get(0));
assertSame(1, bean.stringRepositoryMap.size());
assertSame(1, bean.integerRepositoryMap.size());
assertSame(gr, bean.stringRepositoryMap.get("genericRepo"));
assertSame(gr, bean.integerRepositoryMap.get("genericRepo"));
}
@Test
public void testGenericsBasedConstructorInjectionWithNonGenericTarget() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
SimpleRepository ngr = new SimpleRepository();
bf.registerSingleton("simpleRepo", ngr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
assertSame(ngr, bean.stringRepository);
assertSame(ngr, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length);
assertSame(1, bean.integerRepositoryArray.length);
assertSame(ngr, bean.stringRepositoryArray[0]);
assertSame(ngr, bean.integerRepositoryArray[0]);
assertSame(1, bean.stringRepositoryList.size());
assertSame(1, bean.integerRepositoryList.size());
assertSame(ngr, bean.stringRepositoryList.get(0));
assertSame(ngr, bean.integerRepositoryList.get(0));
assertSame(1, bean.stringRepositoryMap.size());
assertSame(1, bean.integerRepositoryMap.size());
assertSame(ngr, bean.stringRepositoryMap.get("simpleRepo"));
assertSame(ngr, bean.integerRepositoryMap.get("simpleRepo"));
}
@Test
public void testGenericsBasedConstructorInjectionWithMixedTargets() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
StringRepository sr = new StringRepository();
bf.registerSingleton("stringRepo", sr);
GenericRepository gr = new GenericRepositorySubclass();
bf.registerSingleton("genericRepo", gr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
assertSame(sr, bean.stringRepository);
assertSame(gr, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length);
assertSame(1, bean.integerRepositoryArray.length);
assertSame(sr, bean.stringRepositoryArray[0]);
assertSame(gr, bean.integerRepositoryArray[0]);
assertSame(1, bean.stringRepositoryList.size());
assertSame(1, bean.integerRepositoryList.size());
assertSame(sr, bean.stringRepositoryList.get(0));
assertSame(gr, bean.integerRepositoryList.get(0));
assertSame(1, bean.stringRepositoryMap.size());
assertSame(1, bean.integerRepositoryMap.size());
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
assertSame(gr, bean.integerRepositoryMap.get("genericRepo"));
}
@Test
public void testGenericsBasedConstructorInjectionWithMixedTargetsIncludingNonGeneric() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
StringRepository sr = new StringRepository();
bf.registerSingleton("stringRepo", sr);
SimpleRepository ngr = new SimpleRepositorySubclass();
bf.registerSingleton("simpleRepo", ngr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
assertSame(sr, bean.stringRepository);
assertSame(ngr, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length);
assertSame(1, bean.integerRepositoryArray.length);
assertSame(sr, bean.stringRepositoryArray[0]);
assertSame(ngr, bean.integerRepositoryArray[0]);
assertSame(1, bean.stringRepositoryList.size());
assertSame(1, bean.integerRepositoryList.size());
assertSame(sr, bean.stringRepositoryList.get(0));
assertSame(ngr, bean.integerRepositoryList.get(0));
assertSame(1, bean.stringRepositoryMap.size());
assertSame(1, bean.integerRepositoryMap.size());
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
assertSame(ngr, bean.integerRepositoryMap.get("simpleRepo"));
}
public static class ResourceInjectionBean { public static class ResourceInjectionBean {
...@@ -1859,6 +1983,18 @@ public class AutowiredAnnotationBeanPostProcessorTests { ...@@ -1859,6 +1983,18 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public static class IntegerRepository implements Repository<Integer> { public static class IntegerRepository implements Repository<Integer> {
} }
public static class GenericRepository<T> implements Repository<T> {
}
public static class GenericRepositorySubclass extends GenericRepository {
}
public static class SimpleRepository implements Repository {
}
public static class SimpleRepositorySubclass extends SimpleRepository {
}
public static class RepositoryFieldInjectionBean { public static class RepositoryFieldInjectionBean {
......
...@@ -251,27 +251,13 @@ public abstract class GenericTypeResolver { ...@@ -251,27 +251,13 @@ public abstract class GenericTypeResolver {
/** /**
* Resolve the specified generic type against the given TypeVariable map. * Resolve the specified generic type against the given TypeVariable map.
* @param genericType the generic type to resolve * @param genericType the generic type to resolve
* @param typeVariableMap the TypeVariable Map to resolved against * @param map the TypeVariable Map to resolved against
* @return the type if it resolves to a Class, or {@code Object.class} otherwise * @return the type if it resolves to a Class, or {@code Object.class} otherwise
* @deprecated as of Spring 4.0 in favor of {@link ResolvableType} * @deprecated as of Spring 4.0 in favor of {@link ResolvableType}
*/ */
@Deprecated @Deprecated
public static Class<?> resolveType(Type genericType, final Map<TypeVariable, Type> typeVariableMap) { public static Class<?> resolveType(Type genericType, Map<TypeVariable, Type> map) {
return ResolvableType.forType(genericType, new TypeVariableMapVariableResolver(map)).resolve(Object.class);
ResolvableType.VariableResolver variableResolver = new ResolvableType.VariableResolver() {
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
Type type = typeVariableMap.get(variable);
return (type == null ? null : ResolvableType.forType(type));
}
@Override
public Object getSource() {
return typeVariableMap;
}
};
return ResolvableType.forType(genericType, variableResolver).resolve(Object.class);
} }
/** /**
...@@ -315,4 +301,26 @@ public abstract class GenericTypeResolver { ...@@ -315,4 +301,26 @@ public abstract class GenericTypeResolver {
} }
} }
@SuppressWarnings("serial")
private static class TypeVariableMapVariableResolver implements ResolvableType.VariableResolver {
private final Map<TypeVariable, Type> typeVariableMap;
public TypeVariableMapVariableResolver(Map<TypeVariable, Type> typeVariableMap) {
this.typeVariableMap = typeVariableMap;
}
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
Type type = this.typeVariableMap.get(variable);
return (type != null ? ResolvableType.forType(type) : null);
}
@Override
public Object getSource() {
return this.typeVariableMap;
}
}
} }
...@@ -265,7 +265,8 @@ public class MethodParameter { ...@@ -265,7 +265,8 @@ public class MethodParameter {
Type type = getGenericParameterType(); Type type = getGenericParameterType();
if (type instanceof ParameterizedType) { if (type instanceof ParameterizedType) {
Integer index = getTypeIndexForCurrentLevel(); Integer index = getTypeIndexForCurrentLevel();
Type arg = ((ParameterizedType) type).getActualTypeArguments()[index != null ? index : 0]; Type[] args = ((ParameterizedType) type).getActualTypeArguments();
Type arg = args[index != null ? index : args.length - 1];
if (arg instanceof Class) { if (arg instanceof Class) {
return (Class) arg; return (Class) arg;
} }
......
...@@ -105,8 +105,7 @@ public final class ResolvableType implements Serializable { ...@@ -105,8 +105,7 @@ public final class ResolvableType implements Serializable {
private boolean isResolved = false; private boolean isResolved = false;
/** /**
* Late binding stored copy of the resolved value (valid when {@link #isResolved} is * Late binding stored copy of the resolved value (valid when {@link #isResolved} is true).
* true).
*/ */
private Class<?> resolved; private Class<?> resolved;
...@@ -116,7 +115,6 @@ public final class ResolvableType implements Serializable { ...@@ -116,7 +115,6 @@ public final class ResolvableType implements Serializable {
private final ResolvableType componentType; private final ResolvableType componentType;
/** /**
* Private constructor used to create a new {@link ResolvableType}. * Private constructor used to create a new {@link ResolvableType}.
* @param type the underlying java type (may only be {@code null} for {@link #NONE}) * @param type the underlying java type (may only be {@code null} for {@link #NONE})
...@@ -131,13 +129,25 @@ public final class ResolvableType implements Serializable { ...@@ -131,13 +129,25 @@ public final class ResolvableType implements Serializable {
/** /**
* Return the underling java {@link Type} being managed. With the exception of * Return the underling Java {@link Type} being managed. With the exception of
* the {@link #NONE} constant, this method will never return {@code null}. * the {@link #NONE} constant, this method will never return {@code null}.
*/ */
public Type getType() { public Type getType() {
return this.type; return this.type;
} }
/**
* Return the underlying Java {@link Class} being managed, if available;
* otherwise {@code null}.
*/
public Class<?> getRawClass() {
Type rawType = this.type;
if (rawType instanceof ParameterizedType) {
rawType = ((ParameterizedType) rawType).getRawType();
}
return (rawType instanceof Class ? (Class) rawType : null);
}
/** /**
* Determines if this {@code ResolvableType} is assignable from the specified * Determines if this {@code ResolvableType} is assignable from the specified
* {@code type}. Attempts to follow the same rules as the Java compiler, considering * {@code type}. Attempts to follow the same rules as the Java compiler, considering
...@@ -161,8 +171,7 @@ public final class ResolvableType implements Serializable { ...@@ -161,8 +171,7 @@ public final class ResolvableType implements Serializable {
// Deal with array by delegating to the component type // Deal with array by delegating to the component type
if (isArray()) { if (isArray()) {
return (type.isArray() && getComponentType().isAssignableFrom( return (type.isArray() && getComponentType().isAssignableFrom(type.getComponentType()));
type.getComponentType()));
} }
// Deal with wildcard bounds // Deal with wildcard bounds
...@@ -171,8 +180,8 @@ public final class ResolvableType implements Serializable { ...@@ -171,8 +180,8 @@ public final class ResolvableType implements Serializable {
// in the from X is assignable to <? extends Number> // in the from X is assignable to <? extends Number>
if (typeBounds != null) { if (typeBounds != null) {
return (ourBounds != null && ourBounds.isSameKind(typeBounds) return (ourBounds != null && ourBounds.isSameKind(typeBounds) &&
&& ourBounds.isAssignableFrom(typeBounds.getBounds())); ourBounds.isAssignableFrom(typeBounds.getBounds()));
} }
// in the form <? extends Number> is assignable to X ... // in the form <? extends Number> is assignable to X ...
...@@ -189,8 +198,7 @@ public final class ResolvableType implements Serializable { ...@@ -189,8 +198,7 @@ public final class ResolvableType implements Serializable {
// Recursively check each generic // Recursively check each generic
for (int i = 0; i < getGenerics().length; i++) { for (int i = 0; i < getGenerics().length; i++) {
rtn &= getGeneric(i).isAssignableFrom( rtn &= getGeneric(i).isAssignableFrom(type.as(resolve(Object.class)).getGeneric(i), true);
type.as(resolve(Object.class)).getGeneric(i), true);
} }
return rtn; return rtn;
...@@ -228,8 +236,7 @@ public final class ResolvableType implements Serializable { ...@@ -228,8 +236,7 @@ public final class ResolvableType implements Serializable {
return forType(componentType, this.variableResolver); return forType(componentType, this.variableResolver);
} }
if (this.type instanceof GenericArrayType) { if (this.type instanceof GenericArrayType) {
return forType(((GenericArrayType) this.type).getGenericComponentType(), return forType(((GenericArrayType) this.type).getGenericComponentType(), this.variableResolver);
this.variableResolver);
} }
return resolveType().getComponentType(); return resolveType().getComponentType();
} }
...@@ -291,7 +298,7 @@ public final class ResolvableType implements Serializable { ...@@ -291,7 +298,7 @@ public final class ResolvableType implements Serializable {
* @see #getInterfaces() * @see #getInterfaces()
*/ */
public ResolvableType getSuperType() { public ResolvableType getSuperType() {
final Class<?> resolved = resolve(); Class<?> resolved = resolve();
if (resolved == null || resolved.getGenericSuperclass() == null) { if (resolved == null || resolved.getGenericSuperclass() == null) {
return NONE; return NONE;
} }
...@@ -305,7 +312,7 @@ public final class ResolvableType implements Serializable { ...@@ -305,7 +312,7 @@ public final class ResolvableType implements Serializable {
* @see #getSuperType() * @see #getSuperType()
*/ */
public ResolvableType[] getInterfaces() { public ResolvableType[] getInterfaces() {
final Class<?> resolved = resolve(); Class<?> resolved = resolve();
if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) { if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) {
return EMPTY_TYPES_ARRAY; return EMPTY_TYPES_ARRAY;
} }
...@@ -321,6 +328,35 @@ public final class ResolvableType implements Serializable { ...@@ -321,6 +328,35 @@ public final class ResolvableType implements Serializable {
return (getGenerics().length > 0); return (getGenerics().length > 0);
} }
/**
* Determine whether the underlying type has unresolvable generics:
* either through an unresolvable type variable on the type itself
* or through implementing a generic interface in a raw fashion,
* i.e. without substituting that interface's type variables.
* The result will be {@code true} only in those two scenarios.
*/
public boolean hasUnresolvableGenerics() {
ResolvableType[] generics = getGenerics();
for (ResolvableType generic : generics) {
if (generic.resolve() == null) {
return true;
}
}
Class<?> resolved = resolve();
Type[] ifcs = resolved.getGenericInterfaces();
for (Type ifc : ifcs) {
if (ifc instanceof Class) {
if (forClass((Class) ifc).hasGenerics()) {
return true;
}
}
}
if (resolved.getGenericSuperclass() != null) {
return getSuperType().hasUnresolvableGenerics();
}
return false;
}
/** /**
* Return a {@link ResolvableType} for the specified nesting level. See * Return a {@link ResolvableType} for the specified nesting level. See
* {@link #getNested(int, Map)} for details. * {@link #getNested(int, Map)} for details.
...@@ -362,8 +398,7 @@ public final class ResolvableType implements Serializable { ...@@ -362,8 +398,7 @@ public final class ResolvableType implements Serializable {
while (result != ResolvableType.NONE && !result.hasGenerics()) { while (result != ResolvableType.NONE && !result.hasGenerics()) {
result = result.getSuperType(); result = result.getSuperType();
} }
Integer index = (typeIndexesPerLevel == null ? null Integer index = (typeIndexesPerLevel != null ? typeIndexesPerLevel.get(i) : null);
: typeIndexesPerLevel.get(i));
index = (index == null ? result.getGenerics().length - 1 : index); index = (index == null ? result.getGenerics().length - 1 : index);
result = result.getGeneric(index); result = result.getGeneric(index);
} }
...@@ -515,10 +550,8 @@ public final class ResolvableType implements Serializable { ...@@ -515,10 +550,8 @@ public final class ResolvableType implements Serializable {
* it cannot be serialized. * it cannot be serialized.
*/ */
ResolvableType resolveType() { ResolvableType resolveType() {
if (this.type instanceof ParameterizedType) { if (this.type instanceof ParameterizedType) {
return forType(((ParameterizedType) this.type).getRawType(), return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver);
this.variableResolver);
} }
if (this.type instanceof WildcardType) { if (this.type instanceof WildcardType) {
...@@ -535,7 +568,7 @@ public final class ResolvableType implements Serializable { ...@@ -535,7 +568,7 @@ public final class ResolvableType implements Serializable {
// Try default variable resolution // Try default variable resolution
if (this.variableResolver != null) { if (this.variableResolver != null) {
ResolvableType resolved = this.variableResolver.resolveVariable(variable); ResolvableType resolved = this.variableResolver.resolveVariable(variable);
if(resolved != null) { if (resolved != null) {
return resolved; return resolved;
} }
} }
...@@ -555,7 +588,6 @@ public final class ResolvableType implements Serializable { ...@@ -555,7 +588,6 @@ public final class ResolvableType implements Serializable {
} }
private ResolvableType resolveVariable(TypeVariable<?> variable) { private ResolvableType resolveVariable(TypeVariable<?> variable) {
if (this.type instanceof TypeVariable) { if (this.type instanceof TypeVariable) {
return resolveType().resolveVariable(variable); return resolveType().resolveVariable(variable);
} }
...@@ -571,8 +603,7 @@ public final class ResolvableType implements Serializable { ...@@ -571,8 +603,7 @@ public final class ResolvableType implements Serializable {
} }
if (parameterizedType.getOwnerType() != null) { if (parameterizedType.getOwnerType() != null) {
return forType(parameterizedType.getOwnerType(), return forType(parameterizedType.getOwnerType(), this.variableResolver).resolveVariable(variable);
this.variableResolver).resolveVariable(variable);
} }
} }
...@@ -627,7 +658,7 @@ public final class ResolvableType implements Serializable { ...@@ -627,7 +658,7 @@ public final class ResolvableType implements Serializable {
} }
/** /**
* Custom serialization support for {@value #NONE}. * Custom serialization support for {@link #NONE}.
*/ */
private Object readResolve() throws ObjectStreamException { private Object readResolve() throws ObjectStreamException {
return (this.type == null ? NONE : this); return (this.type == null ? NONE : this);
...@@ -640,22 +671,7 @@ public final class ResolvableType implements Serializable { ...@@ -640,22 +671,7 @@ public final class ResolvableType implements Serializable {
if (this == NONE) { if (this == NONE) {
return null; return null;
} }
return new DefaultVariableResolver();
return new VariableResolver() {
private static final long serialVersionUID = 1L;
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
return ResolvableType.this.resolveVariable(variable);
}
@Override
public Object getSource() {
return ResolvableType.this;
}
};
} }
private static boolean variableResolverSourceEquals(VariableResolver o1, VariableResolver o2) { private static boolean variableResolverSourceEquals(VariableResolver o1, VariableResolver o2) {
...@@ -842,8 +858,7 @@ public final class ResolvableType implements Serializable { ...@@ -842,8 +858,7 @@ public final class ResolvableType implements Serializable {
* @see #forMethodParameter(Method, int, Class) * @see #forMethodParameter(Method, int, Class)
* @see #forMethodParameter(MethodParameter) * @see #forMethodParameter(MethodParameter)
*/ */
public static ResolvableType forMethodParameter(Method method, int parameterIndex, public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class<?> implementationClass) {
Class<?> implementationClass) {
Assert.notNull(method, "Method must not be null"); Assert.notNull(method, "Method must not be null");
MethodParameter methodParameter = new MethodParameter(method, parameterIndex); MethodParameter methodParameter = new MethodParameter(method, parameterIndex);
methodParameter.setContainingClass(implementationClass); methodParameter.setContainingClass(implementationClass);
...@@ -858,8 +873,7 @@ public final class ResolvableType implements Serializable { ...@@ -858,8 +873,7 @@ public final class ResolvableType implements Serializable {
*/ */
public static ResolvableType forMethodParameter(MethodParameter methodParameter) { public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
Assert.notNull(methodParameter, "MethodParameter must not be null"); Assert.notNull(methodParameter, "MethodParameter must not be null");
ResolvableType owner = forType(methodParameter.getContainingClass()).as( ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass());
methodParameter.getDeclaringClass());
return forType(SerializableTypeWrapper.forMethodParameter(methodParameter), return forType(SerializableTypeWrapper.forMethodParameter(methodParameter),
owner.asVariableResolver()).getNested(methodParameter.getNestingLevel(), owner.asVariableResolver()).getNested(methodParameter.getNestingLevel(),
methodParameter.typeIndexesPerLevel); methodParameter.typeIndexesPerLevel);
...@@ -877,15 +891,13 @@ public final class ResolvableType implements Serializable { ...@@ -877,15 +891,13 @@ public final class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* generics.
* @param sourceClass the source class * @param sourceClass the source class
* @param generics the generics of the class * @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics * @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, ResolvableType...) * @see #forClassWithGenerics(Class, ResolvableType...)
*/ */
public static ResolvableType forClassWithGenerics(Class<?> sourceClass, public static ResolvableType forClassWithGenerics(Class<?> sourceClass, Class<?>... generics) {
Class<?>... generics) {
Assert.notNull(sourceClass, "Source class must not be null"); Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null"); Assert.notNull(generics, "Generics must not be null");
ResolvableType[] resolvableGenerics = new ResolvableType[generics.length]; ResolvableType[] resolvableGenerics = new ResolvableType[generics.length];
...@@ -896,44 +908,17 @@ public final class ResolvableType implements Serializable { ...@@ -896,44 +908,17 @@ public final class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* generics.
* @param sourceClass the source class * @param sourceClass the source class
* @param generics the generics of the class * @param generics the generics of the class
* @return a {@link ResolvableType} for the specific class and generics * @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, Class...) * @see #forClassWithGenerics(Class, Class...)
*/ */
public static ResolvableType forClassWithGenerics(Class<?> sourceClass, public static ResolvableType forClassWithGenerics(Class<?> sourceClass, ResolvableType... generics) {
final ResolvableType... generics) {
Assert.notNull(sourceClass, "Source class must not be null"); Assert.notNull(sourceClass, "Source class must not be null");
Assert.notNull(generics, "Generics must not be null"); Assert.notNull(generics, "Generics must not be null");
final TypeVariable<?>[] typeVariables = sourceClass.getTypeParameters(); TypeVariable<?>[] typeVariables = sourceClass.getTypeParameters();
Assert.isTrue(typeVariables.length == generics.length, return forType(sourceClass, new TypeVariablesVariableResolver(typeVariables, generics));
"Missmatched number of generics specified");
VariableResolver variableResolver = new VariableResolver() {
private static final long serialVersionUID = 1L;
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
for (int i = 0; i < typeVariables.length; i++) {
if(typeVariables[i].equals(variable)) {
return generics[i];
}
}
return null;
}
@Override
public Object getSource() {
return generics;
}
};
return forType(sourceClass, variableResolver);
} }
/** /**
...@@ -948,9 +933,8 @@ public final class ResolvableType implements Serializable { ...@@ -948,9 +933,8 @@ public final class ResolvableType implements Serializable {
} }
/** /**
* Return a {@link ResolvableType} for the specified {@link Type} backed by the * Return a {@link ResolvableType} for the specified {@link Type} backed by the given
* given owner type. NOTE: The resulting {@link ResolvableType} may not be * owner type. NOTE: The resulting {@link ResolvableType} may not be {@link Serializable}.
* {@link Serializable}.
* @param type the source type or {@code null} * @param type the source type or {@code null}
* @param owner the owner type used to resolve variables * @param owner the owner type used to resolve variables
* @return a {@link ResolvableType} for the specified {@link Type} and owner * @return a {@link ResolvableType} for the specified {@link Type} and owner
...@@ -972,7 +956,7 @@ public final class ResolvableType implements Serializable { ...@@ -972,7 +956,7 @@ public final class ResolvableType implements Serializable {
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
*/ */
static ResolvableType forType(Type type, VariableResolver variableResolver) { static ResolvableType forType(Type type, VariableResolver variableResolver) {
if(type == null) { if (type == null) {
return NONE; return NONE;
} }
// Check the cache, we may have a ResolvableType that may have already been resolved // Check the cache, we may have a ResolvableType that may have already been resolved
...@@ -1002,7 +986,51 @@ public final class ResolvableType implements Serializable { ...@@ -1002,7 +986,51 @@ public final class ResolvableType implements Serializable {
* @return the resolved variable or {@code null} * @return the resolved variable or {@code null}
*/ */
ResolvableType resolveVariable(TypeVariable<?> variable); ResolvableType resolveVariable(TypeVariable<?> variable);
}
@SuppressWarnings("serial")
private class DefaultVariableResolver implements VariableResolver {
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
return ResolvableType.this.resolveVariable(variable);
}
@Override
public Object getSource() {
return ResolvableType.this;
}
}
@SuppressWarnings("serial")
private static class TypeVariablesVariableResolver implements VariableResolver {
private final TypeVariable[] typeVariables;
private final ResolvableType[] generics;
public TypeVariablesVariableResolver(TypeVariable[] typeVariables, ResolvableType[] generics) {
Assert.isTrue(typeVariables.length == generics.length, "Mismatched number of generics specified");
this.typeVariables = typeVariables;
this.generics = generics;
}
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
for (int i = 0; i < this.typeVariables.length; i++) {
if (this.typeVariables[i].equals(variable)) {
return this.generics[i];
}
}
return null;
}
@Override
public Object getSource() {
return this.generics;
}
} }
...@@ -1016,12 +1044,12 @@ public final class ResolvableType implements Serializable { ...@@ -1016,12 +1044,12 @@ public final class ResolvableType implements Serializable {
private final ResolvableType[] bounds; private final ResolvableType[] bounds;
/** /**
* Private constructor to create a new {@link WildcardBounds} instance. * Internal constructor to create a new {@link WildcardBounds} instance.
* @param kind the kind of bounds * @param kind the kind of bounds
* @param bounds the bounds * @param bounds the bounds
* @see #get(ResolvableType) * @see #get(ResolvableType)
*/ */
private WildcardBounds(Kind kind, ResolvableType[] bounds) { public WildcardBounds(Kind kind, ResolvableType[] bounds) {
this.kind = kind; this.kind = kind;
this.bounds = bounds; this.bounds = bounds;
} }
...@@ -1079,8 +1107,7 @@ public final class ResolvableType implements Serializable { ...@@ -1079,8 +1107,7 @@ public final class ResolvableType implements Serializable {
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds(); Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds();
ResolvableType[] resolvableBounds = new ResolvableType[bounds.length]; ResolvableType[] resolvableBounds = new ResolvableType[bounds.length];
for (int i = 0; i < bounds.length; i++) { for (int i = 0; i < bounds.length; i++) {
resolvableBounds[i] = ResolvableType.forType(bounds[i], resolvableBounds[i] = ResolvableType.forType(bounds[i], type.variableResolver);
type.variableResolver);
} }
return new WildcardBounds(boundsType, resolvableBounds); return new WildcardBounds(boundsType, resolvableBounds);
} }
...@@ -1089,7 +1116,6 @@ public final class ResolvableType implements Serializable { ...@@ -1089,7 +1116,6 @@ public final class ResolvableType implements Serializable {
* The various kinds of bounds. * The various kinds of bounds.
*/ */
static enum Kind {UPPER, LOWER} static enum Kind {UPPER, LOWER}
} }
} }
...@@ -48,24 +48,21 @@ import org.junit.runner.RunWith; ...@@ -48,24 +48,21 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.ResolvableType.VariableResolver; import org.springframework.core.ResolvableType.VariableResolver;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import static org.mockito.BDDMockito.*;
import static org.mockito.Mockito.*;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/** /**
* Tests for {@link ResolvableType}.
*
* @author Phillip Webb * @author Phillip Webb
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ResolvableTypeTests { public class ResolvableTypeTests {
@Rule @Rule
public ExpectedException thrown = ExpectedException.none(); public ExpectedException thrown = ExpectedException.none();
...@@ -1070,7 +1067,7 @@ public class ResolvableTypeTests { ...@@ -1070,7 +1067,7 @@ public class ResolvableTypeTests {
@Test @Test
public void forClassWithMismatchedGenerics() throws Exception { public void forClassWithMismatchedGenerics() throws Exception {
thrown.expect(IllegalArgumentException.class); thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Missmatched number of generics specified"); thrown.expectMessage("Mismatched number of generics specified");
ResolvableType.forClassWithGenerics(Map.class, Integer.class); ResolvableType.forClassWithGenerics(Map.class, Integer.class);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册