GH-3253: Scan BF hierarchy for BeanDefinition

Fixes https://github.com/spring-projects/spring-integration/issues/3253

The `IntegrationFlowBeanPostProcessor` uses a `containsBean()`
and then `getBeanDefinition()` to be sure that we don't override already
existing bean even if it is created in the parent(s) context.
The problem that `containsBean()` check the hierarchy, but `getBeanDefinition()`
doesn't.
So, we fail with `NoSuchBeanDefinitionException` if bean exists in the parent ctx

* Introduce an utility `IntegrationContextUtils.getBeanDefinition()` to
scan `BeanFactory` recursively for `BeanDefinition` for the requested `name`
* Use this tool in the `IntegrationFlowBeanPostProcessor` logic

**Cherry-pick to 5.2.x & 5.1.x**
This commit is contained in:
Artem Bilan
2020-04-23 17:03:09 -04:00
committed by Gary Russell
parent e73e08b968
commit 4092427878
2 changed files with 37 additions and 7 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,9 @@ package org.springframework.integration.context;
import java.util.Properties;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
@@ -212,4 +215,26 @@ public abstract class IntegrationContextUtils {
return properties;
}
/**
* Return a {@link BeanDefinition} with the given name,
* obtained from the given {@link BeanFactory} or one of its parents.
* @param name the bean name to return
* @param beanFactory the {@link ConfigurableListableBeanFactory} to travers.
* @return the {@link BeanDefinition} for a given name
* @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} is not found
* @since 5.1.10
*/
public static BeanDefinition getBeanDefinition(String name, ConfigurableListableBeanFactory beanFactory) {
try {
return beanFactory.getBeanDefinition(name);
}
catch (NoSuchBeanDefinitionException ex) {
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory instanceof ConfigurableListableBeanFactory) {
return getBeanDefinition(name, (ConfigurableListableBeanFactory) parentBeanFactory);
}
throw ex;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@ import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.ComponentsRegistration;
import org.springframework.integration.dsl.ConsumerEndpointSpec;
@@ -138,7 +139,8 @@ public class IntegrationFlowBeanPostProcessor
}
}
private Object processStandardIntegrationFlow(StandardIntegrationFlow flow, String flowBeanName) { // NOSONAR complexity
private Object processStandardIntegrationFlow(StandardIntegrationFlow flow, String flowBeanName) { // NOSONAR
// complexity
String flowNamePrefix = flowBeanName + ".";
if (this.flowContext == null) {
this.flowContext = this.beanFactory.getBean(IntegrationFlowContext.class);
@@ -283,7 +285,9 @@ public class IntegrationFlowBeanPostProcessor
String beanNameToUse = entry.getValue();
if (StringUtils.hasText(beanNameToUse) &&
ConfigurableBeanFactory.SCOPE_PROTOTYPE.equals(
this.beanFactory.getBeanDefinition(beanNameToUse).getScope())) {
IntegrationContextUtils.getBeanDefinition(beanNameToUse, this.beanFactory)
.getScope())) {
this.beanFactory.initializeBean(componentToUse, beanNameToUse);
}
targetIntegrationComponents.put(component, beanNameToUse);
@@ -393,7 +397,8 @@ public class IntegrationFlowBeanPostProcessor
String beanName = ((NamedComponent) instance).getBeanName();
if (beanName != null) {
if (this.beanFactory.containsBean(beanName)) {
BeanDefinition existingBeanDefinition = this.beanFactory.getBeanDefinition(beanName);
BeanDefinition existingBeanDefinition =
IntegrationContextUtils.getBeanDefinition(beanName, this.beanFactory);
if (!ConfigurableBeanFactory.SCOPE_PROTOTYPE.equals(existingBeanDefinition.getScope())
&& !instance.equals(this.beanFactory.getBean(beanName))) {
@@ -429,8 +434,8 @@ public class IntegrationFlowBeanPostProcessor
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition((Class<Object>) component.getClass(), () -> component)
.applyCustomizers(customizers)
.getRawBeanDefinition();
.applyCustomizers(customizers)
.getRawBeanDefinition();
if (parentName != null && this.beanFactory.containsBeanDefinition(parentName)) {
AbstractBeanDefinition parentBeanDefinition =