INT-3916: Don't Use CTOR Injection in FactoryBean

JIRA: https://jira.spring.io/browse/INT-3916

The `JpaOutboundGatewayFactoryBean` used CTOR injection for the `JpaExecutor`.
That one, in turn, uses CTOR injection for the `EntityManagerFactory`.

Such a dependency may cause the `early bean instantiating` in case of `AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck()`.
And we end up with the `BeanCurrentlyInCreationException`.

Therefore no one `FactoryBean` should use CTOR injection if there is a potential hierarchical dependency.

NOTE: there is no tests on the matter, since we don't change the components behavior.
The `JPA` sample application will be changed to the Boot to track this fix.

**Cherry-pick to 4.2.x**

Address PR comments and fix other `FactoryBean`s for the same issue, when it is reasonable

Polishing

Address PR comments

Make setter `setSockJsTaskScheduler` as `public`
This commit is contained in:
Artem Bilan
2015-12-17 18:05:31 -05:00
committed by Gary Russell
parent 2be12160e9
commit fec2a36f42
15 changed files with 310 additions and 149 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jpa.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
@@ -24,7 +27,6 @@ import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jpa.outbound.JpaOutboundGatewayFactoryBean;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* The Abstract Parser for the JPA Outbound Gateways.
@@ -42,14 +44,13 @@ public abstract class AbstractJpaOutboundGatewayParser extends AbstractConsumerE
@Override
protected BeanDefinitionBuilder parseHandler(Element gatewayElement, ParserContext parserContext) {
final BeanDefinitionBuilder jpaOutboundGatewayBuilder = BeanDefinitionBuilder
.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
final BeanDefinitionBuilder jpaOutboundGatewayBuilder =
BeanDefinitionBuilder.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaOutboundGatewayBuilder, gatewayElement, "reply-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaOutboundGatewayBuilder, gatewayElement, "requires-reply");
final String replyChannel = gatewayElement.getAttribute("reply-channel");
String replyChannel = gatewayElement.getAttribute("reply-channel");
if (StringUtils.hasText(replyChannel)) {
jpaOutboundGatewayBuilder.addPropertyReference("outputChannel", replyChannel);
@@ -58,7 +59,8 @@ public abstract class AbstractJpaOutboundGatewayParser extends AbstractConsumerE
final Element transactionalElement = DomUtils.getChildElementByTagName(gatewayElement, "transactional");
if (transactionalElement != null) {
BeanDefinition txAdviceDefinition = IntegrationNamespaceUtils.configureTransactionAttributes(transactionalElement);
BeanDefinition txAdviceDefinition =
IntegrationNamespaceUtils.configureTransactionAttributes(transactionalElement);
ManagedList<BeanDefinition> adviceChain = new ManagedList<BeanDefinition>();
adviceChain.add(txAdviceDefinition);
jpaOutboundGatewayBuilder.addPropertyValue("txAdviceChain", adviceChain);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2016 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jpa.config.xml;
import org.w3c.dom.Element;
@@ -33,6 +34,7 @@ import org.springframework.util.xml.DomUtils;
*
* @author Amol Nayak
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.2
*
@@ -52,7 +54,8 @@ public class JpaOutboundChannelAdapterParser extends AbstractOutboundChannelAdap
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
final BeanDefinitionBuilder jpaOutboundChannelAdapterBuilder = BeanDefinitionBuilder.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
final BeanDefinitionBuilder jpaOutboundChannelAdapterBuilder =
BeanDefinitionBuilder.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getJpaExecutorBuilder(element, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "persist-mode");
@@ -60,21 +63,25 @@ public class JpaOutboundChannelAdapterParser extends AbstractOutboundChannelAdap
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "flush-size");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "clear-on-flush");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "parameter-source-factory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element, "use-payload-as-parameter-source");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, element,
"use-payload-as-parameter-source");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String channelAdapterId = this.resolveId(element, jpaOutboundChannelAdapterBuilder.getRawBeanDefinition(), parserContext);
final String channelAdapterId = resolveId(element, jpaOutboundChannelAdapterBuilder.getRawBeanDefinition(),
parserContext);
final String jpaExecutorBeanName = channelAdapterId + ".jpaExecutor";
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
parserContext.registerBeanComponent(
new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
jpaOutboundChannelAdapterBuilder.addConstructorArgReference(jpaExecutorBeanName);
jpaOutboundChannelAdapterBuilder.addPropertyValue("producesReply", Boolean.FALSE);
jpaOutboundChannelAdapterBuilder.addPropertyReference("jpaExecutor", jpaExecutorBeanName)
.addPropertyValue("producesReply", Boolean.FALSE);
final Element transactionalElement = DomUtils.getChildElementByTagName(element, "transactional");
if(transactionalElement != null) {
BeanDefinition txAdviceDefinition = IntegrationNamespaceUtils.configureTransactionAttributes(transactionalElement);
BeanDefinition txAdviceDefinition =
IntegrationNamespaceUtils.configureTransactionAttributes(transactionalElement);
ManagedList<BeanDefinition> adviceChain = new ManagedList<BeanDefinition>();
adviceChain.add(txAdviceDefinition);
jpaOutboundChannelAdapterBuilder.addPropertyValue("txAdviceChain", adviceChain);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jpa.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.jpa.support.OutboundGatewayType;
import org.springframework.util.CollectionUtils;
@@ -34,6 +33,7 @@ import org.springframework.util.xml.DomUtils;
*
* @author Amol Nayak
* @author Gunnar Hillert
* @author Artem Bilan
* @since 2.2
*/
public class RetrievingJpaOutboundGatewayParser extends AbstractJpaOutboundGatewayParser {
@@ -43,7 +43,8 @@ public class RetrievingJpaOutboundGatewayParser extends AbstractJpaOutboundGatew
final BeanDefinitionBuilder jpaOutboundGatewayBuilder = super.parseHandler(gatewayElement, parserContext);
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getOutboundGatewayJpaExecutorBuilder(gatewayElement, parserContext);
final BeanDefinitionBuilder jpaExecutorBuilder =
JpaParserUtils.getOutboundGatewayJpaExecutorBuilder(gatewayElement, parserContext);
BeanDefinition firstResultExpression = IntegrationNamespaceUtils
.createExpressionDefinitionFromValueOrExpression("first-result", "first-result-expression",
@@ -59,8 +60,7 @@ public class RetrievingJpaOutboundGatewayParser extends AbstractJpaOutboundGatew
jpaExecutorBuilder.addPropertyValue("maxResultsExpression", maxResultsExpression);
}
String idExpression = gatewayElement.getAttribute("id-expression");
if (StringUtils.hasText(idExpression)) {
if (StringUtils.hasText(gatewayElement.getAttribute("id-expression"))) {
String[] otherAttributes = {"jpa-query", "native-query", "named-query", "first-result",
"first-result-expression", "max-results", "max-results-expression", "delete-in-batch",
"expect-single-result", "parameter-source-factory", "use-payload-as-parameter-source"};
@@ -84,27 +84,26 @@ public class RetrievingJpaOutboundGatewayParser extends AbstractJpaOutboundGatew
+ "not allowed with an 'id-expression' attribute.",
gatewayElement);
}
AbstractBeanDefinition idExpressionDef = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class)
.addConstructorArgValue(idExpression)
.getBeanDefinition();
BeanDefinition idExpressionDef =
IntegrationNamespaceUtils.createExpressionDefIfAttributeDefined("id-expression", gatewayElement);
jpaExecutorBuilder.addPropertyValue("idExpression", idExpressionDef);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "delete-after-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "flush-after-delete", "flush");
IntegrationNamespaceUtils.setValueIfAttributeDefined(
jpaExecutorBuilder, gatewayElement, "flush-after-delete", "flush");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "delete-in-batch");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "expect-single-result");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String gatewayId = this.resolveId(gatewayElement, jpaOutboundGatewayBuilder.getRawBeanDefinition(), parserContext);
final String gatewayId = resolveId(gatewayElement, jpaOutboundGatewayBuilder.getRawBeanDefinition(), parserContext);
final String jpaExecutorBeanName = gatewayId + ".jpaExecutor";
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
parserContext.registerBeanComponent(
new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
jpaOutboundGatewayBuilder.addConstructorArgReference(jpaExecutorBeanName);
jpaOutboundGatewayBuilder.addPropertyValue("gatewayType", OutboundGatewayType.RETRIEVING);
return jpaOutboundGatewayBuilder;
return jpaOutboundGatewayBuilder.addPropertyReference("jpaExecutor", jpaExecutorBeanName)
.addPropertyValue("gatewayType", OutboundGatewayType.RETRIEVING);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2016 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.
@@ -29,6 +29,7 @@ import org.springframework.integration.jpa.support.OutboundGatewayType;
*
* @author Amol Nayak
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.2
*
@@ -37,10 +38,10 @@ public class UpdatingJpaOutboundGatewayParser extends AbstractJpaOutboundGateway
@Override
protected BeanDefinitionBuilder parseHandler(Element gatewayElement, ParserContext parserContext) {
BeanDefinitionBuilder jpaOutboundGatewayBuilder = super.parseHandler(gatewayElement, parserContext);
final BeanDefinitionBuilder jpaOutboundGatewayBuilder = super.parseHandler(gatewayElement, parserContext);
final BeanDefinitionBuilder jpaExecutorBuilder = JpaParserUtils.getOutboundGatewayJpaExecutorBuilder(gatewayElement, parserContext);
BeanDefinitionBuilder jpaExecutorBuilder =
JpaParserUtils.getOutboundGatewayJpaExecutorBuilder(gatewayElement, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "persist-mode");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "flush");
@@ -48,15 +49,15 @@ public class UpdatingJpaOutboundGatewayParser extends AbstractJpaOutboundGateway
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaExecutorBuilder, gatewayElement, "clear-on-flush");
final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
final String gatewayId = this.resolveId(gatewayElement, jpaOutboundGatewayBuilder.getRawBeanDefinition(), parserContext);
final String gatewayId = resolveId(gatewayElement, jpaOutboundGatewayBuilder.getRawBeanDefinition(),
parserContext);
final String jpaExecutorBeanName = gatewayId + ".jpaExecutor";
parserContext.registerBeanComponent(new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
jpaOutboundGatewayBuilder.addConstructorArgReference(jpaExecutorBeanName);
jpaOutboundGatewayBuilder.addPropertyValue("gatewayType", OutboundGatewayType.UPDATING);
return jpaOutboundGatewayBuilder;
parserContext.registerBeanComponent(
new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));
return jpaOutboundGatewayBuilder.addPropertyReference("jpaExecutor", jpaExecutorBeanName)
.addPropertyValue("gatewayType", OutboundGatewayType.UPDATING);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 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.
@@ -28,7 +28,6 @@ import org.springframework.integration.jpa.support.OutboundGatewayType;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
@@ -47,21 +46,21 @@ import org.springframework.util.CollectionUtils;
*/
public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHandler> {
private final JpaExecutor jpaExecutor;
private JpaExecutor jpaExecutor;
private OutboundGatewayType gatewayType = OutboundGatewayType.UPDATING;
/**
* &lt;transactional /&gt; element applies to entire flow from this point
*/
private volatile List<Advice> txAdviceChain;
private List<Advice> txAdviceChain;
/**
* &lt;request-handler-advice-chain /&gt; only applies to the handleRequestMessage.
*/
private volatile List<Advice> adviceChain;
private List<Advice> adviceChain;
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private boolean producesReply = true;
@@ -71,18 +70,26 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
private long replyTimeout;
private volatile boolean requiresReply = false;
private boolean requiresReply = false;
private volatile String componentName;
private String componentName;
public JpaOutboundGatewayFactoryBean() {
}
/**
* Constructor taking an {@link JpaExecutor} that wraps all JPA Operations.
*
* @param jpaExecutor Must not be null
*
* @deprecated since {@literal 4.2.5} in favor of {@link #setJpaExecutor(JpaExecutor)}
* to avoid {@code BeanCurrentlyInCreationException}
* during {@code AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck()}
*/
@Deprecated
public JpaOutboundGatewayFactoryBean(JpaExecutor jpaExecutor) {
Assert.notNull(jpaExecutor, "jpaExecutor must not be null.");
this.jpaExecutor = jpaExecutor;
}
public void setJpaExecutor(JpaExecutor jpaExecutor) {
this.jpaExecutor = jpaExecutor;
}
@@ -112,9 +119,9 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
/**
* Specifies the time the gateway will wait to send the result to the reply channel.
* Only applies when the reply channel itself might block the send (for example a bounded QueueChannel that is currently full).
* Only applies when the reply channel itself might block the send
* (for example a bounded QueueChannel that is currently full).
* By default the Gateway will wait indefinitely.
*
* @param replyTimeout The timeout in milliseconds
*/
public void setReplyTimeout(long replyTimeout) {
@@ -127,13 +134,18 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
/**
* Sets the name of the handler component.
*
* @param componentName The component name.
*/
public void setComponentName(String componentName) {
this.componentName = componentName;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
super.setBeanClassLoader(classLoader);
this.beanClassLoader = classLoader;
}
@Override
public Class<?> getObjectType() {
return MessageHandler.class;
@@ -141,7 +153,6 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
@Override
protected MessageHandler createInstance() {
JpaOutboundGateway jpaOutboundGateway = new JpaOutboundGateway(jpaExecutor);
jpaOutboundGateway.setGatewayType(this.gatewayType);
jpaOutboundGateway.setProducesReply(this.producesReply);
@@ -169,4 +180,5 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
return jpaOutboundGateway;
}
}