diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java index 084a3d0572..ceac102ab8 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -21,6 +21,7 @@ import java.util.Set; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.Advised; +import org.springframework.beans.factory.DisposableBean; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -41,21 +42,23 @@ import org.springframework.util.StringUtils; * @author David Liu */ public abstract class AbstractStandardMessageHandlerFactoryBean - extends AbstractSimpleMessageHandlerFactoryBean { + extends AbstractSimpleMessageHandlerFactoryBean implements DisposableBean { private static final ExpressionParser expressionParser = new SpelExpressionParser(); private static final Set referencedReplyProducers = new HashSet<>(); - private volatile Boolean requiresReply; + private Boolean requiresReply; - private volatile Object targetObject; + private Object targetObject; - private volatile String targetMethodName; + private String targetMethodName; - private volatile Expression expression; + private Expression expression; - private volatile Long sendTimeout; + private Long sendTimeout; + + private MessageHandler replyHandler; /** * Set the target POJO for the message handler. @@ -101,6 +104,13 @@ public abstract class AbstractStandardMessageHandlerFactoryBean return this.sendTimeout; } + @Override + public void destroy() { + if (this.replyHandler != null) { + referencedReplyProducers.remove(this.replyHandler); + } + } + @Override protected MessageHandler createHandler() { MessageHandler handler; @@ -158,6 +168,7 @@ public abstract class AbstractStandardMessageHandlerFactoryBean "An AbstractMessageProducingMessageHandler may only be referenced once (" + replyHandler.getComponentName() + ") - use scope=\"prototype\""); referencedReplyProducers.add(replyHandler); + this.replyHandler = replyHandler; } /** diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/dsl/DslIntegrationConfigurationInitializer.java b/spring-integration-core/src/main/java/org/springframework/integration/config/dsl/DslIntegrationConfigurationInitializer.java index 2f572a2c6d..ec4ef87088 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/dsl/DslIntegrationConfigurationInitializer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/dsl/DslIntegrationConfigurationInitializer.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.integration.config.IntegrationConfigurationInitializer; import org.springframework.integration.dsl.IntegrationComponentSpec; +import org.springframework.integration.dsl.IntegrationFlowDefinition; import org.springframework.integration.dsl.context.IntegrationFlowContext; import org.springframework.util.Assert; @@ -46,6 +47,9 @@ public class DslIntegrationConfigurationInitializer implements IntegrationConfig private static final String INTEGRATION_FLOW_CONTEXT_BEAN_NAME = Introspector.decapitalize(IntegrationFlowContext.class.getName()); + private static final String INTEGRATION_FLOW_REPLY_PRODUCER_CLEANER_BEAN_NAME = + Introspector.decapitalize(IntegrationFlowDefinition.ReplyProducerCleaner.class.getName()); + @Override public void initialize(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { Assert.isInstanceOf(BeanDefinitionRegistry.class, configurableListableBeanFactory, @@ -59,6 +63,8 @@ public class DslIntegrationConfigurationInitializer implements IntegrationConfig new RootBeanDefinition(IntegrationFlowBeanPostProcessor.class)); registry.registerBeanDefinition(INTEGRATION_FLOW_CONTEXT_BEAN_NAME, new RootBeanDefinition(IntegrationFlowContext.class)); + registry.registerBeanDefinition(INTEGRATION_FLOW_REPLY_PRODUCER_CLEANER_BEAN_NAME, + new RootBeanDefinition(IntegrationFlowDefinition.ReplyProducerCleaner.class)); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowDefinition.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowDefinition.java index 746a1151ae..f29c81bb35 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowDefinition.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowDefinition.java @@ -29,7 +29,9 @@ import org.reactivestreams.Publisher; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.integration.aggregator.AggregatingMessageHandler; @@ -122,9 +124,9 @@ public abstract class IntegrationFlowDefinition integrationComponents = new LinkedHashMap<>(); - protected MessageChannel currentMessageChannel; + private MessageChannel currentMessageChannel; - protected Object currentComponent; + private Object currentComponent; private StandardIntegrationFlow integrationFlow; @@ -481,7 +483,9 @@ public abstract class IntegrationFlowDefinition> endpointConfigurer) { + public B transform(String expression, + Consumer> endpointConfigurer) { + Assert.hasText(expression, "'expression' must not be empty"); return transform(new ExpressionEvaluatingTransformer(PARSER.parseExpression(expression)), endpointConfigurer); @@ -2773,4 +2777,21 @@ public abstract class IntegrationFlowDefinition replyProducers = + TestUtils.getPropertyValue(flowBuilder, "REFERENCED_REPLY_PRODUCERS", Set.class); + + assertTrue(replyProducers.contains(bridgeHandler)); + flowRegistration.destroy(); + + assertFalse(replyProducers.contains(bridgeHandler)); } @Test @@ -183,12 +201,13 @@ public class ManualFlowTests { .addBean(additionalBean) .register(); + String flowRegistrationId = flowRegistration.getId(); BeanFactoryHandler bean = - this.beanFactory.getBean(flowRegistration.getId() + BeanFactoryHandler.class.getName() + "#0", + this.beanFactory.getBean(flowRegistrationId + BeanFactoryHandler.class.getName() + "#0", BeanFactoryHandler.class); assertSame(additionalBean, bean); assertSame(this.beanFactory, bean.beanFactory); - bean = this.beanFactory.getBean(flowRegistration.getId() + "." + "anId.handler", BeanFactoryHandler.class); + bean = this.beanFactory.getBean(flowRegistrationId + "." + "anId.handler", BeanFactoryHandler.class); MessagingTemplate messagingTemplate = flowRegistration.getMessagingTemplate(); messagingTemplate.setReceiveTimeout(10000); @@ -210,9 +229,9 @@ public class ManualFlowTests { flowRegistration.destroy(); - assertFalse(this.beanFactory.containsBean(flowRegistration.getId())); - assertFalse(this.beanFactory.containsBean(flowRegistration.getId() + ".input")); - assertFalse(this.beanFactory.containsBean(flowRegistration.getId() + BeanFactoryHandler.class.getName() + "#0")); + assertFalse(this.beanFactory.containsBean(flowRegistrationId)); + assertFalse(this.beanFactory.containsBean(flowRegistrationId + ".input")); + assertFalse(this.beanFactory.containsBean(flowRegistrationId + BeanFactoryHandler.class.getName() + "#0")); ThreadPoolTaskScheduler taskScheduler = this.beanFactory.getBean(ThreadPoolTaskScheduler.class); Thread.sleep(100);