INT-3250: Add @EnableIntegration Infrastructure

JIRA: https://jira.springsource.org/browse/INT-3250

* The main purpose of this change to move all infrastructure bean definitions
from `AbstractIntegrationNamespaceHandler` to a new `IntegrationRegistrar`.
* `IntegrationRegistrar` is invoked by the standard `@Configuration` process and also directly from `AbstractIntegrationNamespaceHandler`.
* Provide some refactoring for consistency.
* Add AnnotationContext tests

INT-3250: Polishing

* Remove `@EnableIntegration#defaultPublisherChannel`
* Revert `#jsonPath` & `#xpath` registration logic
* Fix for DEBUG message on each parse

JIRA: https://jira.springsource.org/browse/INT-3258

INT-3250 Fix JavaDocs & IDEA+Gradle srcDirs issue

INT-3250: Revert `build.gradle` changes

INT-3250 Polishing

* Remove commented out code
* Add javadoc to IntegrationRegistrar.registerBeanDefinitions()
* Fix javadoc links to other Spring projects (and JVM, JEE)
* Add reference documentation and what's new
This commit is contained in:
Artem Bilan
2013-12-30 13:21:28 +02:00
committed by Gary Russell
parent 4ac1a9113a
commit 4ae5152eff
11 changed files with 586 additions and 283 deletions

View File

@@ -32,6 +32,19 @@ allprojects {
maven { url 'http://repo.spring.io/plugins-release' }
mavenCentral()
}
ext.javadocLinks = [
'http://docs.oracle.com/javase/7/docs/api/',
'http://docs.oracle.com/javaee/6/api/',
'http://docs.spring.io/spring/docs/current/javadoc-api/',
'http://docs.spring.io/spring-amqp/docs/latest-ga/api/',
'http://docs.spring.io/spring-data-gemfire/docs/current/api/',
'http://docs.spring.io/spring-data/data-mongo/docs/current/api/',
'http://docs.spring.io/spring-data/data-redis/docs/current/api/',
'http://docs.spring.io/spring-social-twitter/docs/current/api/',
'http://docs.spring.io/spring-ws/sites/2.0/apidocs/'
] as String[]
}
subprojects { subproject ->
@@ -614,6 +627,7 @@ task api(type: Javadoc) {
options.header = rootProject.description
options.overview = 'src/api/overview.html'
options.stylesheetFile = file("src/api/stylesheet.css")
options.links(project.ext.javadocLinks)
source subprojects.collect { project ->
project.sourceSets.main.allJava
}

View File

@@ -0,0 +1,320 @@
/*
* Copyright 2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.config;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor;
import org.springframework.integration.channel.DefaultHeaderChannelRegistry;
import org.springframework.integration.config.annotation.EnableIntegration;
import org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.expression.IntegrationEvaluationContextAwareBeanPostProcessor;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* {@link ImportBeanDefinitionRegistrar} implementation that configures integration infrastructure.
*
* @author Artem Bilan
* @since 4.0
*/
public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {
private static final Log logger = LogFactory.getLog(IntegrationRegistrar.class);
private static final Set<Integer> registriesProcessed = new HashSet<Integer>();
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Invoked by the framework when an &#64;EnableIntegration annotation is encountered.
* Also called with {@code null} {@code importingClassMetadata} from {@code AbstractIntegrationNamespaceHandler}
* to register the same beans when using XML configuration. Also called by {@code AnnotationConfigParser}
* to register the messaging annotation post processors (for {@code <int:annotation-config/>}).
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
this.registerImplicitChannelCreator(registry);
this.registerIntegrationEvaluationContext(registry);
this.registerIntegrationProperties(registry);
this.registerHeaderChannelRegistry(registry);
this.registerBuiltInBeans(registry);
this.registerDefaultConfiguringBeanFactoryPostProcessor(registry);
if (importingClassMetadata != null) {
this.registerMessagingAnnotationPostProcessors(importingClassMetadata, registry);
}
}
/**
* This method will auto-register a ChannelInitializer which could also be overridden by the user
* by simply registering a ChannelInitializer {@code <bean>} with its {@code autoCreate} property
* set to false to suppress channel creation.
* It will also register a ChannelInitializer$AutoCreateCandidatesCollector which simply collects candidate channel names.
*
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
*/
private void registerImplicitChannelCreator(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME)) {
String channelsAutoCreateExpression = IntegrationProperties.getExpressionFor(IntegrationProperties.CHANNELS_AUTOCREATE);
BeanDefinitionBuilder channelDef = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml.ChannelInitializer")
.addPropertyValue("autoCreate", channelsAutoCreateExpression);
BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(),
IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, registry);
}
if (!registry.containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) {
BeanDefinitionBuilder channelRegistryBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml.ChannelInitializer$AutoCreateCandidatesCollector");
channelRegistryBuilder.addConstructorArgValue(new ManagedSet<String>());
BeanDefinitionHolder channelRegistryHolder = new BeanDefinitionHolder(channelRegistryBuilder.getBeanDefinition(),
IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, registry);
}
}
/**
* Register {@code integrationGlobalProperties} bean if necessary.
*
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
*/
private void registerIntegrationProperties(BeanDefinitionRegistry registry) {
boolean alreadyRegistered = false;
if (registry instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) registry)
.containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME);
}
else {
alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME);
}
if (!alreadyRegistered) {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(this.classLoader);
try {
Resource[] defaultResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.default.properties");
Resource[] userResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.properties");
List<Resource> resources = new LinkedList<Resource>(Arrays.asList(defaultResources));
resources.addAll(Arrays.asList(userResources));
BeanDefinitionBuilder integrationPropertiesBuilder = BeanDefinitionBuilder
.genericBeanDefinition(PropertiesFactoryBean.class)
.addPropertyValue("locations", resources);
registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME,
integrationPropertiesBuilder.getBeanDefinition());
}
catch (IOException e) {
logger.warn("Cannot load 'spring.integration.properties' Resources.", e);
}
}
}
/**
* Register {@link IntegrationEvaluationContextFactoryBean} bean
* and {@link IntegrationEvaluationContextAwareBeanPostProcessor}, if necessary.
*
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
*/
private void registerIntegrationEvaluationContext(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)) {
BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationEvaluationContextFactoryBean.class);
integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionHolder integrationEvaluationContextHolder =
new BeanDefinitionHolder(integrationEvaluationContextBuilder.getBeanDefinition(),
IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder,
registry);
RootBeanDefinition integrationEvalContextBPP = new RootBeanDefinition(IntegrationEvaluationContextAwareBeanPostProcessor.class);
BeanDefinitionReaderUtils.registerWithGeneratedName(integrationEvalContextBPP, registry);
}
}
/**
* Register {@code jsonPath} and {@code xpath} SpEL-function beans, if necessary.
*
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
*/
private void registerBuiltInBeans(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
String jsonPathBeanName = "jsonPath";
boolean alreadyRegistered = false;
if (registry instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) registry).containsBean(jsonPathBeanName);
}
else {
alreadyRegistered = registry.isBeanNameInUse(jsonPathBeanName);
}
if (!alreadyRegistered && !registriesProcessed.contains(registryId)) {
Class<?> jsonPathClass = null;
try {
jsonPathClass = ClassUtils.forName("com.jayway.jsonpath.JsonPath", this.classLoader);
}
catch (ClassNotFoundException e) {
logger.debug("SpEL function '#jsonPath' isn't registered: there is no jayway json-path.jar on the classpath.");
}
if (jsonPathClass != null) {
IntegrationNamespaceUtils.registerSpelFunctionBean(registry, jsonPathBeanName,
IntegrationNamespaceUtils.BASE_PACKAGE + ".json.JsonPathUtils", "evaluate");
}
}
alreadyRegistered = false;
String xpathBeanName = "xpath";
if (registry instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) registry).containsBean(xpathBeanName);
}
else {
alreadyRegistered = registry.isBeanNameInUse(xpathBeanName);
}
if (!alreadyRegistered && !registriesProcessed.contains(registryId)) {
Class<?> xpathClass = null;
try {
xpathClass = ClassUtils.forName(IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils",
this.classLoader);
}
catch (ClassNotFoundException e) {
logger.debug("SpEL function '#xpath' isn't registered: there is no spring-integration-xml.jar on the classpath.");
}
if (xpathClass != null) {
IntegrationNamespaceUtils.registerSpelFunctionBean(registry, xpathBeanName,
IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate");
}
}
registriesProcessed.add(registryId);
}
/**
* Register {@code DefaultConfiguringBeanFactoryPostProcessor}, if necessary.
*
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
*/
private void registerDefaultConfiguringBeanFactoryPostProcessor(BeanDefinitionRegistry registry) {
boolean alreadyRegistered = false;
if (registry instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) registry).containsBean(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
}
else {
alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
}
if (!alreadyRegistered) {
BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml.DefaultConfiguringBeanFactoryPostProcessor");
BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder(
postProcessorBuilder.getBeanDefinition(), IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(postProcessorHolder, registry);
}
}
/**
* Register a {@link DefaultHeaderChannelRegistry} in the given {@link BeanDefinitionRegistry}, if necessary.
*
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
*/
private void registerHeaderChannelRegistry(BeanDefinitionRegistry registry) {
boolean alreadyRegistered = false;
if (registry instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) registry).containsBean(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
}
else {
alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
}
if (!alreadyRegistered) {
if (logger.isInfoEnabled()) {
logger.info("No bean named '" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME +
"' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.");
}
BeanDefinitionBuilder schedulerBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultHeaderChannelRegistry.class);
BeanDefinitionHolder replyChannelRegistryComponent = new BeanDefinitionHolder(
schedulerBuilder.getBeanDefinition(),
IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(replyChannelRegistryComponent, registry);
}
}
/**
* Register {@link MessagingAnnotationPostProcessor} and {@link PublisherAnnotationBeanPostProcessor}, if necessary.
* Inject {@code defaultPublishedChannel} from provided {@link AnnotationMetadata}, if any.
*
* @param meta The {@link AnnotationMetadata} to get additional properties for {@link org.springframework.beans.factory.config.BeanDefinition}s.
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
*/
private void registerMessagingAnnotationPostProcessors(AnnotationMetadata meta, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME)) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessagingAnnotationPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME, builder.getBeanDefinition());
}
if (!registry.containsBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME)) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PublisherAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
Map<String, Object> attrs = meta.getAnnotationAttributes(EnableIntegration.class.getName());
String defaultPublisherChannel = (String) attrs.get("defaultPublisherChannel");
if (StringUtils.hasText(defaultPublisherChannel)) {
builder.addPropertyReference("defaultChannel", defaultPublisherChannel);
}
registry.registerBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME, builder.getBeanDefinition());
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.config.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.integration.config.IntegrationRegistrar;
/**
* Add this annotation to an {@code @Configuration} class to have
* the imported Spring Integration configuration :
* <pre class="code">
* &#064;Configuration
* &#064;EnableIntegration
* &#064;ComponentScan(basePackageClasses = { MyConfiguration.class })
* public class MyIntegrationConfiguration {
* }
* </pre>
*
* @author Artem Bilan
* @since 4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(IntegrationRegistrar.class)
public @interface EnableIntegration {
}

View File

@@ -33,6 +33,7 @@ import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -122,9 +123,9 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
String inputChannelName = element.getAttribute(inputChannelAttributeName);
if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)) {
if (parserContext.getRegistry().containsBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) {
if (parserContext.getRegistry().containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) {
BeanDefinition channelRegistry = parserContext.getRegistry().
getBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
getBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
ConstructorArgumentValues caValues = channelRegistry.getConstructorArgumentValues();
ValueHolder vh = caValues.getArgumentValue(0, Collection.class);
if (vh == null) { //although it should never happen if it does we can fix it
@@ -137,7 +138,7 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
}
else {
parserContext.getReaderContext().error("Failed to locate '" +
ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME + "'", parserContext.getRegistry());
IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME + "'", parserContext.getRegistry());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -16,40 +16,19 @@
package org.springframework.integration.config.xml;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.integration.channel.DefaultHeaderChannelRegistry;
import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean;
import org.springframework.integration.config.xml.ChannelInitializer.AutoCreateCandidatesCollector;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.expression.IntegrationEvaluationContextAwareBeanPostProcessor;
import org.springframework.util.ClassUtils;
import org.springframework.integration.config.IntegrationRegistrar;
import org.springframework.util.StringUtils;
/**
@@ -59,6 +38,7 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*/
public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHandler {
@@ -66,238 +46,21 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa
private static final String VERSION = "4.0";
public static final String CHANNEL_INITIALIZER_BEAN_NAME = "channelInitializer";
private static final String DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME =
"DefaultConfiguringBeanFactoryPostProcessor";
private static final String DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME =
IntegrationNamespaceUtils.BASE_PACKAGE + ".internal" + DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME;
private final NamespaceHandlerDelegate delegate = new NamespaceHandlerDelegate();
@Override
public final BeanDefinition parse(Element element, ParserContext parserContext) {
this.verifySchemaVersion(element, parserContext);
this.registerImplicitChannelCreator(parserContext);
this.registerIntegrationEvaluationContext(parserContext);
this.registerIntegrationProperties(parserContext);
this.registerHeaderChannelRegistry(parserContext);
this.registerBuiltInBeans(parserContext);
this.registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(parserContext);
new IntegrationRegistrar().registerBeanDefinitions(null, parserContext.getRegistry());
return this.delegate.parse(element, parserContext);
}
private void registerIntegrationProperties(ParserContext parserContext) {
boolean alreadyRegistered = false;
BeanDefinitionRegistry registry = parserContext.getRegistry();
if (registry instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) registry)
.containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME);
}
else {
alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME);
}
if (!alreadyRegistered) {
ResourcePatternResolver resourceResolver =
new PathMatchingResourcePatternResolver(parserContext.getReaderContext().getBeanClassLoader());
try {
Resource[] defaultResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.default.properties");
Resource[] userResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.properties");
List<Resource> resources = new LinkedList<Resource>(Arrays.asList(defaultResources));
resources.addAll(Arrays.asList(userResources));
BeanDefinitionBuilder integrationPropertiesBuilder = BeanDefinitionBuilder
.genericBeanDefinition(PropertiesFactoryBean.class)
.addPropertyValue("locations", resources);
registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME,
integrationPropertiesBuilder.getBeanDefinition());
}
catch (IOException e) {
parserContext.getReaderContext().warning("Cannot load 'spring.integration.properties' Resources.", null, e);
}
}
}
@Override
public final BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext) {
return this.delegate.decorate(source, definition, parserContext);
}
/*
* This method will auto-register a ChannelInitializer which could also be overridden by the user
* by simply registering a ChannelInitializer <bean> with its 'autoCreate' property set to false to suppress channel creation.
* It will also register a ChannelInitializer$AutoCreateCandidatesCollector which simply collects candidate channel names.
*/
private void registerImplicitChannelCreator(ParserContext parserContext) {
// ChannelInitializer
boolean alreadyRegistered = false;
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
// unlike DefaultConfiguringBeanFactoryPostProcessor we need one of these per registry
// therefore we need to call containsBeanDefinition(..) which does not consider parent registry
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBeanDefinition(CHANNEL_INITIALIZER_BEAN_NAME);
}
else {
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(CHANNEL_INITIALIZER_BEAN_NAME);
}
if (!alreadyRegistered) {
String channelsAutoCreateExpression = IntegrationProperties.getExpressionFor(IntegrationProperties.CHANNELS_AUTOCREATE);
BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(ChannelInitializer.class)
.addPropertyValue("autoCreate", channelsAutoCreateExpression);
BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), CHANNEL_INITIALIZER_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, parserContext.getRegistry());
}
// ChannelInitializer$AutoCreateCandidatesCollector
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
// unlike DefaultConfiguringBeanFactoryPostProcessor, we need one of these per registry
// therefore we need to call containsBeanDefinition(..) which does not consider the parent registry
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).
containsBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
}
else {
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
}
if (!alreadyRegistered) {
BeanDefinitionBuilder channelRegistryBuilder = BeanDefinitionBuilder.genericBeanDefinition(AutoCreateCandidatesCollector.class);
channelRegistryBuilder.addConstructorArgValue(new ManagedSet<String>());
BeanDefinitionHolder channelRegistryHolder =
new BeanDefinitionHolder(channelRegistryBuilder.getBeanDefinition(), ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, parserContext.getRegistry());
}
}
private void registerIntegrationEvaluationContext(ParserContext parserContext) {
boolean alreadyRegistered = false;
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
// unlike DefaultConfiguringBeanFactoryPostProcessor, we need one of these per registry
// therefore we need to call containsBeanDefinition(..) which does not consider the parent registry
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBeanDefinition(
IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME);
}
else {
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(
IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME);
}
if (!alreadyRegistered) {
BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationEvaluationContextFactoryBean.class);
integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionHolder integrationEvaluationContextHolder = new BeanDefinitionHolder(
integrationEvaluationContextBuilder.getBeanDefinition(),
IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder,
parserContext.getRegistry());
RootBeanDefinition integrationEvalContextBPP = new RootBeanDefinition(
IntegrationEvaluationContextAwareBeanPostProcessor.class);
BeanDefinitionReaderUtils.registerWithGeneratedName(integrationEvalContextBPP, parserContext.getRegistry());
}
}
private void registerBuiltInBeans(ParserContext parserContext) {
String jsonPathBeanName = "jsonPath";
boolean alreadyRegistered = false;
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBean(jsonPathBeanName);
}
else {
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(jsonPathBeanName);
}
if (!alreadyRegistered) {
Class<?> jsonPathClass = null;
try {
jsonPathClass = ClassUtils.forName("com.jayway.jsonpath.JsonPath", parserContext.getReaderContext().getBeanClassLoader());
}
catch (ClassNotFoundException e) {
logger.debug("SpEL function '#jsonPath' isn't registered: there is no jayway json-path.jar on the classpath.");
}
if (jsonPathClass != null) {
IntegrationNamespaceUtils.registerSpelFunctionBean(parserContext.getRegistry(), jsonPathBeanName,
IntegrationNamespaceUtils.BASE_PACKAGE + ".json.JsonPathUtils", "evaluate");
}
}
String xpathBeanName = "xpath";
alreadyRegistered = false;
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBean(xpathBeanName);
}
else {
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(xpathBeanName);
}
if (!alreadyRegistered) {
Class<?> xpathClass = null;
try {
xpathClass = ClassUtils.forName(IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils",
parserContext.getReaderContext().getBeanClassLoader());
}
catch (ClassNotFoundException e) {
logger.debug("SpEL function '#xpath' isn't registered: there is no spring-integration-xml.jar on the classpath.");
}
if (xpathClass != null) {
IntegrationNamespaceUtils.registerSpelFunctionBean(parserContext.getRegistry(), xpathBeanName,
IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate");
}
}
this.doRegisterBuiltInBeans(parserContext);
}
protected void doRegisterBuiltInBeans(ParserContext parserContext) {
}
private void registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(ParserContext parserContext) {
boolean alreadyRegistered = false;
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBean(DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
}
else {
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
}
if (!alreadyRegistered) {
BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml." + DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME);
BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder(
postProcessorBuilder.getBeanDefinition(), DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(postProcessorHolder, parserContext.getRegistry());
}
}
/**
* Register a DefaultHeaderChannelRegistry in the given BeanDefinitionRegistry, if necessary.
*/
private void registerHeaderChannelRegistry(ParserContext parserContext) {
boolean alreadyRegistered = false;
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry())
.containsBean(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
}
else {
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(
IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
}
if (!alreadyRegistered) {
if (logger.isInfoEnabled()) {
logger.info("No bean named '" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME +
"' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.");
}
BeanDefinitionBuilder schedulerBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultHeaderChannelRegistry.class);
BeanDefinitionHolder replyChannelRegistryComponent = new BeanDefinitionHolder(
schedulerBuilder.getBeanDefinition(),
IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(replyChannelRegistryComponent, parserContext.getRegistry());
}
}
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator decorator) {
this.delegate.doRegisterBeanDefinitionDecorator(elementName, decorator);
}
@@ -346,4 +109,5 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2014 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.
@@ -16,42 +16,36 @@
package org.springframework.integration.config.xml;
import java.util.Collections;
import java.util.Map;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.integration.config.IntegrationRegistrar;
/**
* Parser for the &lt;annotation-config&gt; element of the integration namespace.
* Adds a {@link org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor}
* and a {@link org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor}
* to the application context.
*
* Just delegate the real configuration to the {@link IntegrationRegistrar}.
*
* @author Mark Fisher
* @author Artem Bilan
*/
public class AnnotationConfigParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition messagingAnnotationPostProcessorDef = new RootBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.annotation.MessagingAnnotationPostProcessor");
messagingAnnotationPostProcessorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String messagingAnnotationPostProcessorName = IntegrationNamespaceUtils.BASE_PACKAGE + ".internalMessagingAnnotationPostProcessor";
parserContext.getRegistry().registerBeanDefinition(messagingAnnotationPostProcessorName, messagingAnnotationPostProcessorDef);
RootBeanDefinition publisherAnnotationPostProcessorDef = new RootBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".aop.PublisherAnnotationBeanPostProcessor");
publisherAnnotationPostProcessorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String defaultPublisherChannel = element.getAttribute("default-publisher-channel");
if (StringUtils.hasText(defaultPublisherChannel)) {
publisherAnnotationPostProcessorDef.getPropertyValues().add("defaultChannel", new RuntimeBeanReference(defaultPublisherChannel));
}
String publisherAnnotationPostProcessorName = IntegrationNamespaceUtils.BASE_PACKAGE + ".internalPublisherAnnotationBeanPostProcessor";
parserContext.getRegistry().registerBeanDefinition(publisherAnnotationPostProcessorName, publisherAnnotationPostProcessorDef);
public BeanDefinition parse(final Element element, ParserContext parserContext) {
new IntegrationRegistrar().registerBeanDefinitions(new StandardAnnotationMetadata(Object.class) {
@Override
public Map<String, Object> getAnnotationAttributes(String annotationType) {
return Collections.<String, Object> singletonMap("defaultPublisherChannel", element.getAttribute("default-publisher-channel"));
}
}, parserContext.getRegistry());
return null;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.util.Assert;
/**
@@ -42,8 +43,6 @@ import org.springframework.util.Assert;
*/
final class ChannelInitializer implements BeanFactoryAware, InitializingBean {
public static String AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME = "$autoCreateChannelCandidates";
private Log logger = LogFactory.getLog(this.getClass());
private volatile BeanFactory beanFactory;
@@ -66,9 +65,8 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean {
}
else {
AutoCreateCandidatesCollector channelCandidatesCollector =
beanFactory.getBean(AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, AutoCreateCandidatesCollector.class);
Assert.notNull(channelCandidatesCollector, "Failed to locate '" +
ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
beanFactory.getBean(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, AutoCreateCandidatesCollector.class);
Assert.notNull(channelCandidatesCollector, "Failed to locate '" + IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
// at this point channelNames are all resolved with placeholders and SpEL
Collection<String> channelNames = channelCandidatesCollector.getChannelNames();
if (channelNames != null){
@@ -98,5 +96,7 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean {
public Collection<String> getChannelNames() {
return channelNames;
}
}
}

View File

@@ -21,6 +21,7 @@ import java.util.Properties;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.metadata.MetadataStore;
import org.springframework.messaging.MessageChannel;
import org.springframework.scheduling.TaskScheduler;
@@ -51,6 +52,20 @@ public abstract class IntegrationContextUtils {
public static final String INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME = "integrationGlobalProperties";
public static final String CHANNEL_INITIALIZER_BEAN_NAME = "channelInitializer";
public static final String AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME = "$autoCreateChannelCandidates";
public static final String DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME = "DefaultConfiguringBeanFactoryPostProcessor";
public static final String MESSAGING_ANNOTATION_POSTPROCESSOR_NAME = IntegrationNamespaceUtils.BASE_PACKAGE
+ ".internalMessagingAnnotationPostProcessor";
public static final String PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME = IntegrationNamespaceUtils.BASE_PACKAGE +
".internalPublisherAnnotationBeanPostProcessor";
// public static final String FLOW_POST_PROCESSOR_BEAN_NAME = IntegrationFlowBeanFactoryPostProcessor.class.getSimpleName();
/**
* @param beanFactory BeanFactory for lookup, must not be null.
* @return The {@link MetadataStore} bean whose name is "metadataStore".

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.configuration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Payload;
import org.springframework.integration.annotation.Publisher;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.annotation.EnableIntegration;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
/**
* @author Artem Bilan
* @since 4.0
*/
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class EnableIntegrationTests {
@Autowired
private MessageChannel input;
@Autowired
private PollableChannel output;
@Autowired
private PollableChannel publishedChannel;
@Test
public void testAnnotatedServiceActivator() {
this.input.send(MessageBuilder.withPayload("Foo").build());
Message<?> receive = this.output.receive(1000);
assertNotNull(receive);
assertEquals("FOO", receive.getPayload());
receive = this.publishedChannel.receive(1000);
assertNotNull(receive);
assertEquals("foo", receive.getPayload());
}
@Configuration
@ComponentScan(basePackageClasses = EnableIntegrationTests.class)
@EnableIntegration
public static class ContextConfiguration {
@Bean
public MessageChannel input() {
return new DirectChannel();
}
@Bean
public PollableChannel output() {
return new QueueChannel();
}
@Bean
public PollableChannel publishedChannel() {
return new QueueChannel();
}
}
@MessageEndpoint
public static class AnnotationTestService {
@ServiceActivator(inputChannel = "input", outputChannel = "output")
@Publisher(channel = "publishedChannel")
@Payload("#args[0].toLowerCase()")
public String handle(String payload) {
return payload.toUpperCase();
}
}
}

View File

@@ -318,4 +318,32 @@
</section>
</section>
<section>
<title>Configuration</title>
<para>
Throughout this document you will see references to XML namespace support for declaring elements in a Spring
Integration flow. This support is provided by a series of namespace parsers that generate appropriate
bean definitions to implement a particular component. For example, many endpoints consist of a
<interfacename>MessageHandler</interfacename> bean and a <classname>ConsumerEndpointFactoryBean</classname>
into which the handler and an input channel name are injected.
</para>
<para>
The first time a Spring Integration namespace element is encountered, the framework automatically declares
a number of beans that are used to support the runtime environment (task scheduler,
implicit channel creator, etc).
</para>
<para id="enable-integration">
Starting with <emphasis>version 4.0</emphasis>, these support beans can also be defined when using
<code>@Configuration</code> classes, by adding a new annotation <code>@EnableIntegration</code>.
This is useful when declaring a simple Spring Integration flow using purely Java Configuration.
For example; you can declare an endpoint with a <interfacename>MessageHandler</interfacename> <code>@Bean</code>
as well as a <classname>ConsumerEndpointFactoryBean</classname> <code>@Bean</code>.
</para>
<para>
<code>@EnableIntegration</code> is also useful when you have a parent context with no Spring Integration
components and 2 or more child contexts that do use Spring Integration. It would enable these common
components to be declared once only, in the parent context.
</para>
</section>
</chapter>

View File

@@ -23,15 +23,26 @@
<section id="4.0-general">
<title>General Changes</title>
<para>
Core messaging abstractions (<interfacename>Message</interfacename>,
<interfacename>MessageChannel</interfacename> etc) have moved to the Spring
Framework <code>spring-messaging</code> module. Users who reference these
classes directly in their code will need to make changes as described in
the first section of the
<ulink url="https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-3.0-to-4.0-Migration-Guide"
>Migration Guide</ulink>.
</para>
<section>
<title>Requires Spring Framework 4.0</title>
<para>
Core messaging abstractions (<interfacename>Message</interfacename>,
<interfacename>MessageChannel</interfacename> etc) have moved to the Spring
Framework <code>spring-messaging</code> module. Users who reference these
classes directly in their code will need to make changes as described in
the first section of the
<ulink url="https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-3.0-to-4.0-Migration-Guide"
>Migration Guide</ulink>.
</para>
</section>
<section>
<title>@EnableConfiguration</title>
<para>
The <code>@EnableIntegration</code> annotation has been added, to permit declaration of
standard Spring Integration beans when using <code>@Configuration</code> classes. See
<xref linkend="enable-integration"/> for more information.
</para>
</section>
<section id="4.0-xpath-header-enricher-header-type">
<title>Header Type for XPath Header Enricher</title>
<para>