Support for matching partial generics
Closes gh-20727
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -144,12 +144,16 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
|
|||||||
if (cacheType) {
|
if (cacheType) {
|
||||||
rbd.targetType = targetType;
|
rbd.targetType = targetType;
|
||||||
}
|
}
|
||||||
if (descriptor.fallbackMatchAllowed() &&
|
if (descriptor.fallbackMatchAllowed()) {
|
||||||
(targetType.hasUnresolvableGenerics() || targetType.resolve() == Properties.class)) {
|
|
||||||
// Fallback matches allow unresolvable generics, e.g. plain HashMap to Map<String,String>;
|
// Fallback matches allow unresolvable generics, e.g. plain HashMap to Map<String,String>;
|
||||||
// and pragmatically also java.util.Properties to any Map (since despite formally being a
|
// and pragmatically also java.util.Properties to any Map (since despite formally being a
|
||||||
// Map<Object,Object>, java.util.Properties is usually perceived as a Map<String,String>).
|
// Map<Object,Object>, java.util.Properties is usually perceived as a Map<String,String>).
|
||||||
return true;
|
if (targetType.hasUnresolvableGenerics()) {
|
||||||
|
return dependencyType.isAssignableFromResolvedPart(targetType);
|
||||||
|
}
|
||||||
|
else if (targetType.resolve() == Properties.class) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Full check for complex generic type match...
|
// Full check for complex generic type match...
|
||||||
return dependencyType.isAssignableFrom(targetType);
|
return dependencyType.isAssignableFrom(targetType);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -32,8 +32,8 @@ class Spr16179Tests {
|
|||||||
void repro() {
|
void repro() {
|
||||||
try (AnnotationConfigApplicationContext bf = new AnnotationConfigApplicationContext(AssemblerConfig.class, AssemblerInjection.class)) {
|
try (AnnotationConfigApplicationContext bf = new AnnotationConfigApplicationContext(AssemblerConfig.class, AssemblerInjection.class)) {
|
||||||
assertThat(bf.getBean(AssemblerInjection.class).assembler0).isSameAs(bf.getBean("someAssembler"));
|
assertThat(bf.getBean(AssemblerInjection.class).assembler0).isSameAs(bf.getBean("someAssembler"));
|
||||||
// assertNull(bf.getBean(AssemblerInjection.class).assembler1); TODO: accidental match
|
assertThat(bf.getBean(AssemblerInjection.class).assembler1).isNull();
|
||||||
// assertNull(bf.getBean(AssemblerInjection.class).assembler2);
|
assertThat(bf.getBean(AssemblerInjection.class).assembler2).isSameAs(bf.getBean("pageAssembler"));
|
||||||
assertThat(bf.getBean(AssemblerInjection.class).assembler3).isSameAs(bf.getBean("pageAssembler"));
|
assertThat(bf.getBean(AssemblerInjection.class).assembler3).isSameAs(bf.getBean("pageAssembler"));
|
||||||
assertThat(bf.getBean(AssemblerInjection.class).assembler4).isSameAs(bf.getBean("pageAssembler"));
|
assertThat(bf.getBean(AssemblerInjection.class).assembler4).isSameAs(bf.getBean("pageAssembler"));
|
||||||
assertThat(bf.getBean(AssemblerInjection.class).assembler5).isSameAs(bf.getBean("pageAssembler"));
|
assertThat(bf.getBean(AssemblerInjection.class).assembler5).isSameAs(bf.getBean("pageAssembler"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -265,7 +265,7 @@ public class ResolvableType implements Serializable {
|
|||||||
public boolean isAssignableFrom(Class<?> other) {
|
public boolean isAssignableFrom(Class<?> other) {
|
||||||
// As of 6.1: shortcut assignability check for top-level Class references
|
// As of 6.1: shortcut assignability check for top-level Class references
|
||||||
return (this.type instanceof Class<?> clazz ? ClassUtils.isAssignable(clazz, other) :
|
return (this.type instanceof Class<?> clazz ? ClassUtils.isAssignable(clazz, other) :
|
||||||
isAssignableFrom(forClass(other), false, null));
|
isAssignableFrom(forClass(other), false, null, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -280,10 +280,24 @@ public class ResolvableType implements Serializable {
|
|||||||
* {@code ResolvableType}; {@code false} otherwise
|
* {@code ResolvableType}; {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isAssignableFrom(ResolvableType other) {
|
public boolean isAssignableFrom(ResolvableType other) {
|
||||||
return isAssignableFrom(other, false, null);
|
return isAssignableFrom(other, false, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAssignableFrom(ResolvableType other, boolean strict, @Nullable Map<Type, Type> matchedBefore) {
|
/**
|
||||||
|
* Determine whether this {@code ResolvableType} is assignable from the
|
||||||
|
* specified other type, as far as the other type is actually resolvable.
|
||||||
|
* @param other the type to be checked against (as a {@code ResolvableType})
|
||||||
|
* @return {@code true} if the specified other type can be assigned to this
|
||||||
|
* {@code ResolvableType} as far as it is resolvable; {@code false} otherwise
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
public boolean isAssignableFromResolvedPart(ResolvableType other) {
|
||||||
|
return isAssignableFrom(other, false, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAssignableFrom(ResolvableType other, boolean strict,
|
||||||
|
@Nullable Map<Type, Type> matchedBefore, boolean upUntilUnresolvable) {
|
||||||
|
|
||||||
Assert.notNull(other, "ResolvableType must not be null");
|
Assert.notNull(other, "ResolvableType must not be null");
|
||||||
|
|
||||||
// If we cannot resolve types, we are not assignable
|
// If we cannot resolve types, we are not assignable
|
||||||
@@ -305,7 +319,12 @@ public 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 (other.isArray() && getComponentType().isAssignableFrom(other.getComponentType(), true, matchedBefore));
|
return (other.isArray() && getComponentType().isAssignableFrom(
|
||||||
|
other.getComponentType(), true, matchedBefore, upUntilUnresolvable));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upUntilUnresolvable && other.isUnresolvableTypeVariable()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deal with wildcard bounds
|
// Deal with wildcard bounds
|
||||||
@@ -314,8 +333,15 @@ public class ResolvableType implements Serializable {
|
|||||||
|
|
||||||
// In the form X is assignable to <? extends Number>
|
// In the form X is assignable to <? extends Number>
|
||||||
if (typeBounds != null) {
|
if (typeBounds != null) {
|
||||||
return (ourBounds != null && ourBounds.isSameKind(typeBounds) &&
|
if (ourBounds != null) {
|
||||||
ourBounds.isAssignableFrom(typeBounds.getBounds()));
|
return (ourBounds.isSameKind(typeBounds) && ourBounds.isAssignableFrom(typeBounds.getBounds()));
|
||||||
|
}
|
||||||
|
else if (upUntilUnresolvable) {
|
||||||
|
return typeBounds.isAssignableFrom(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the form <? extends Number> is assignable to X...
|
// In the form <? extends Number> is assignable to X...
|
||||||
@@ -376,7 +402,7 @@ public class ResolvableType implements Serializable {
|
|||||||
}
|
}
|
||||||
matchedBefore.put(this.type, other.type);
|
matchedBefore.put(this.type, other.type);
|
||||||
for (int i = 0; i < ourGenerics.length; i++) {
|
for (int i = 0; i < ourGenerics.length; i++) {
|
||||||
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], true, matchedBefore)) {
|
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], true, matchedBefore, upUntilUnresolvable)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user