From 4092427878fc8b4ef5f061acac7ae53b2e83c808 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 23 Apr 2020 17:03:09 -0400 Subject: [PATCH] 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** --- .../context/IntegrationContextUtils.java | 27 ++++++++++++++++++- .../IntegrationFlowBeanPostProcessor.java | 17 +++++++----- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java index b15210cb64..37d631dbee 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java @@ -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; + } + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/IntegrationFlowBeanPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/IntegrationFlowBeanPostProcessor.java index 6796194fc7..7d40802a55 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/IntegrationFlowBeanPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/IntegrationFlowBeanPostProcessor.java @@ -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) component.getClass(), () -> component) - .applyCustomizers(customizers) - .getRawBeanDefinition(); + .applyCustomizers(customizers) + .getRawBeanDefinition(); if (parentName != null && this.beanFactory.containsBeanDefinition(parentName)) { AbstractBeanDefinition parentBeanDefinition =