Skip to content

Commit

Permalink
Consistent support for generic FactoryBean type matching
Browse files Browse the repository at this point in the history
Closes gh-32590
See gh-32489
  • Loading branch information
jhoeller committed Apr 8, 2024
1 parent 802967f commit f2889b1
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 250 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -525,42 +525,73 @@ protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean a
// Check manually registered singletons.
Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {

// Determine target for FactoryBean match if necessary.
if (beanInstance instanceof FactoryBean<?> factoryBean) {
if (!isFactoryDereference) {
Class<?> type = getTypeForFactoryBean(factoryBean);
return (type != null && typeToMatch.isAssignableFrom(type));
}
else {
return typeToMatch.isInstance(beanInstance);
}
}
else if (!isFactoryDereference) {
if (typeToMatch.isInstance(beanInstance)) {
// Direct match for exposed instance?
return true;
}
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
// Generics potentially only match on the target class, not on the proxy...
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> targetType = mbd.getTargetType();
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
// Check raw class match as well, making sure it's exposed on the proxy.
Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
if (type == null) {
return false;
}
if (typeToMatch.isAssignableFrom(type)) {
return true;
}
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
ResolvableType targetType = mbd.targetType;
if (targetType == null) {
targetType = mbd.factoryMethodReturnType;
}
if (targetType == null) {
return false;
}
if (typeToMatch.isAssignableFrom(targetType)) {
return true;
Class<?> targetClass = targetType.resolve();
if (targetClass != null && FactoryBean.class.isAssignableFrom(targetClass)) {
Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch != null && !FactoryBean.class.isAssignableFrom(classToMatch) &&
!classToMatch.isAssignableFrom(targetType.toClass())) {
return typeToMatch.isAssignableFrom(targetType.getGeneric());
}
}
else {
return typeToMatch.isAssignableFrom(targetType);
}
}
ResolvableType resolvableType = mbd.targetType;
if (resolvableType == null) {
resolvableType = mbd.factoryMethodReturnType;
return false;
}
}
else if (isFactoryDereference) {
return false;
}

// Actual matching against bean instance...
if (typeToMatch.isInstance(beanInstance)) {
// Direct match for exposed instance?
return true;
}
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
// Generics potentially only match on the target class, not on the proxy...
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> targetType = mbd.getTargetType();
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
// Check raw class match as well, making sure it's exposed on the proxy.
Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
return false;
}
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
if (typeToMatch.isAssignableFrom(targetType)) {
return true;
}
}
ResolvableType resolvableType = mbd.targetType;
if (resolvableType == null) {
resolvableType = mbd.factoryMethodReturnType;
}
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
}
else {
return false;
}
return false;
}
else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
// null instance registered
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,6 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc
}
}
}
else {
// Pre-existing target type: In case of a generic FactoryBean type,
// unwrap nested generic type when matching a non-FactoryBean type.
Class<?> resolvedClass = targetType.resolve();
if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) {
Class<?> typeToBeMatched = dependencyType.resolve();
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched) &&
!typeToBeMatched.isAssignableFrom(resolvedClass)) {
targetType = targetType.getGeneric();
if (descriptor.fallbackMatchAllowed()) {
// Matching the Class-based type determination for FactoryBean
// objects in the lazy-determination getType code path below.
targetType = ResolvableType.forClass(targetType.resolve());
}
}
}
}
}

if (targetType == null) {
Expand All @@ -145,6 +128,23 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc
if (cacheType) {
rbd.targetType = targetType;
}

// Pre-declared target type: In case of a generic FactoryBean type,
// unwrap nested generic type when matching a non-FactoryBean type.
Class<?> targetClass = targetType.resolve();
if (targetClass != null && FactoryBean.class.isAssignableFrom(targetClass)) {
Class<?> classToMatch = dependencyType.resolve();
if (classToMatch != null && !FactoryBean.class.isAssignableFrom(classToMatch) &&
!classToMatch.isAssignableFrom(targetClass)) {
targetType = targetType.getGeneric();
if (descriptor.fallbackMatchAllowed()) {
// Matching the Class-based type determination for FactoryBean
// objects in the lazy-determination getType code path above.
targetType = ResolvableType.forClass(targetType.resolve());
}
}
}

if (descriptor.fallbackMatchAllowed() &&
(targetType.hasUnresolvableGenerics() || targetType.resolve() == Properties.class)) {
// Fallback matches allow unresolvable generics, e.g. plain HashMap to Map<String,String>;
Expand Down
Loading

0 comments on commit f2889b1

Please sign in to comment.