INT-3327: Add IntegrationConverter Annotation

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

INT-3327: Addressing PR comments

Polishing
This commit is contained in:
Artem Bilan
2014-03-31 16:38:28 +03:00
committed by Gary Russell
parent d74b4dba73
commit 969310b453
14 changed files with 317 additions and 138 deletions

View File

@@ -27,8 +27,10 @@ import java.lang.annotation.Target;
* annotation will be applied as global channel interceptors
* using the provided {@code patterns} to match channel names.
* <p>
* The annotation can be used at the {@code class} level for {@link org.springframework.stereotype.Component} beans
* This annotation can be used at the {@code class} level for {@link org.springframework.stereotype.Component} beans
* and on methods with {@link org.springframework.context.annotation.Bean}.
* <p>
* This annotation is an analogue of {@code <int:channel-interceptor/>}.
*
* @author Artem Bilan
* @since 4.0

View File

@@ -0,0 +1,42 @@
/*
* 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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* A marker annotation (an analogue of {@code <int:converter/>}) to register
* {@link org.springframework.core.convert.converter.Converter},
* {@link org.springframework.core.convert.converter.GenericConverter} or
* {@link org.springframework.core.convert.converter.ConverterFactory} beans for the {@code integrationConversionService}.
* <p>
* This annotation can be used at the {@code class} level for {@link org.springframework.stereotype.Component} beans
* and on methods with {@link org.springframework.context.annotation.Bean}.
*
* @author Artem Bilan
* @since 4.0
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IntegrationConverter {
}

View File

@@ -0,0 +1,93 @@
/*
* 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.util.Set;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.integration.context.IntegrationContextUtils;
/**
* @author Artem Bilan
* @since 4.0
*/
public class IntegrationConverterInitializer implements IntegrationConfigurationInitializer {
private static final String CONTEXT_PACKAGE = "org.springframework.integration.context.";
@Override
public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
for (String beanName : registry.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
if (beanDefinition instanceof AnnotatedBeanDefinition) {
AnnotationMetadata metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
boolean hasIntegrationConverter = metadata.hasAnnotation(IntegrationConverter.class.getName());
if (!hasIntegrationConverter && beanDefinition.getSource() instanceof MethodMetadata) {
MethodMetadata beanMethod = (MethodMetadata) beanDefinition.getSource();
hasIntegrationConverter = beanMethod.isAnnotated(IntegrationConverter.class.getName());
}
if (hasIntegrationConverter) {
this.registerConverter(registry, new RuntimeBeanReference(beanName));
}
}
}
}
@SuppressWarnings("unchecked")
public void registerConverter(BeanDefinitionRegistry registry, BeanMetadataElement converterBeanDefinition) {
Set<BeanMetadataElement> converters = new ManagedSet<BeanMetadataElement>();
if (!registry.containsBeanDefinition(IntegrationContextUtils.CONVERTER_REGISTRAR_BEAN_NAME)) {
BeanDefinitionBuilder converterRegistrarBuilder = BeanDefinitionBuilder.genericBeanDefinition(
CONTEXT_PACKAGE + "ConverterRegistrar").addConstructorArgValue(converters);
registry.registerBeanDefinition(IntegrationContextUtils.CONVERTER_REGISTRAR_BEAN_NAME,
converterRegistrarBuilder.getBeanDefinition());
if (!registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME)) {
registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME,
new RootBeanDefinition(CONTEXT_PACKAGE + "CustomConversionServiceFactoryBean"));
}
}
else {
BeanDefinition converterRegistrarBeanDefinition = registry
.getBeanDefinition(IntegrationContextUtils.CONVERTER_REGISTRAR_BEAN_NAME);
converters = (Set<BeanMetadataElement>) converterRegistrarBeanDefinition
.getConstructorArgumentValues()
.getIndexedArgumentValues()
.values()
.iterator()
.next()
.getValue();
}
converters.add(converterBeanDefinition);
}
}

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.
@@ -21,56 +21,37 @@ import org.w3c.dom.Element;
import org.springframework.beans.factory.config.RuntimeBeanReference;
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.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.IntegrationConverterInitializer;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Oleg Zhurakousky
* @author Mark Fisher
* @author Artem Bilan
* @since 2.0
*/
public class ConverterParser extends AbstractBeanDefinitionParser {
private final ManagedSet<Object> converters = new ManagedSet<Object>();
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
private final static IntegrationConverterInitializer INTEGRATION_CONVERTER_INITIALIZER = new IntegrationConverterInitializer();
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
this.initializeConversionServiceInfrastructureIfNecessary(parserContext);
BeanDefinitionRegistry registry = parserContext.getRegistry();
BeanComponentDefinition converterDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
if (converterDefinition != null) {
this.converters.add(converterDefinition);
INTEGRATION_CONVERTER_INITIALIZER.registerConverter(registry, converterDefinition);
}
else {
String beanName = element.getAttribute("ref");
Assert.isTrue(StringUtils.hasText(beanName),
"Either a 'ref' attribute pointing to a Converter or a <bean> sub-element defining a Converter is required.");
this.converters.add(new RuntimeBeanReference(beanName));
INTEGRATION_CONVERTER_INITIALIZER.registerConverter(registry, new RuntimeBeanReference(beanName));
}
return null;
}
private void initializeConversionServiceInfrastructureIfNecessary(ParserContext parserContext) {
synchronized (this.initializationMonitor) {
if (!this.initialized) {
String contextPackage = "org.springframework.integration.context.";
BeanDefinitionBuilder creatorBuilder = BeanDefinitionBuilder.rootBeanDefinition(contextPackage + "ConversionServiceCreator");
BeanDefinitionReaderUtils.registerWithGeneratedName(creatorBuilder.getBeanDefinition(), parserContext.getRegistry());
BeanDefinitionBuilder conversionServiceBuilder = BeanDefinitionBuilder.rootBeanDefinition(contextPackage + "ConverterRegistrar");
conversionServiceBuilder.addConstructorArgValue(converters);
BeanDefinitionReaderUtils.registerWithGeneratedName(conversionServiceBuilder.getBeanDefinition(), parserContext.getRegistry());
this.initialized = true;
}
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2002-2010 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.context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
/**
* @author Oleg Zhurakousky
* @author Mark Fisher
* @since 2.0
*/
class ConversionServiceCreator implements BeanFactoryPostProcessor {
private final Log logger = LogFactory.getLog(this.getClass());
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
if (!beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME)) {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionBuilder conversionServiceBuilder = BeanDefinitionBuilder.rootBeanDefinition(CustomConversionServiceFactoryBean.class);
BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(
conversionServiceBuilder.getBeanDefinition(), IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, (BeanDefinitionRegistry) beanFactory);
}
else if (logger.isWarnEnabled()) {
logger.warn("BeanFactory is not a BeanDefinitionRegistry implementation. Cannot register a default ConversionService.");
}
}
}
/**
* This is a workaround until we depend on Spring 3.1 and specifically when SPR-8818 is resolved.
* See INT-2259 and INT-1893 for more detail.
*/
static class CustomConversionServiceFactoryBean extends ConversionServiceFactoryBean {
@Override
public ConversionService getObject() {
ConversionService service = super.getObject();
if (service instanceof GenericConversionService) {
((GenericConversionService) service).removeConvertible(Object.class, Object.class);
}
return service;
}
}
}

View File

@@ -22,7 +22,6 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.util.Assert;
@@ -30,19 +29,19 @@ import org.springframework.util.Assert;
/**
* Utility class that keeps track of a set of Converters in order to register
* them with the "integrationConversionService" upon initialization.
*
*
* @author Oleg Zhurakousky
* @author Mark Fisher
* @since 2.0
*/
class ConverterRegistrar implements InitializingBean, BeanFactoryAware {
private final Set<Converter<?, ?>> converters;
private final Set<?> converters;
private BeanFactory beanFactory;
public ConverterRegistrar(Set<Converter<?, ?>> converters) {
public ConverterRegistrar(Set<?> converters) {
this.converters = converters;
}
@@ -55,7 +54,7 @@ class ConverterRegistrar implements InitializingBean, BeanFactoryAware {
Assert.notNull(beanFactory, "BeanFactory is required");
ConversionService conversionService = IntegrationContextUtils.getConversionService(beanFactory);
if (conversionService instanceof GenericConversionService) {
ConversionServiceFactory.registerConverters(converters, (GenericConversionService) conversionService);
ConversionServiceFactory.registerConverters(converters, (GenericConversionService) conversionService);
}
else {
Assert.notNull(conversionService, "Failed to locate '" + IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME + "'");

View File

@@ -0,0 +1,42 @@
/*
* 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.context;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
/**
* This is a workaround until SPR-8818 will be resolved.
* See INT-2259 and INT-1893 for more detail.
*
* @author Oleg Zhurakousky
* @author Mark Fisher
* @since 2.0
*/
class CustomConversionServiceFactoryBean extends ConversionServiceFactoryBean {
@Override
public ConversionService getObject() {
ConversionService service = super.getObject();
if (service instanceof GenericConversionService) {
((GenericConversionService) service).removeConvertible(Object.class, Object.class);
}
return service;
}
}

View File

@@ -52,6 +52,8 @@ public abstract class IntegrationContextUtils {
public static final String METADATA_STORE_BEAN_NAME = "metadataStore";
public static final String CONVERTER_REGISTRAR_BEAN_NAME = "converterRegistrar";
public static final String INTEGRATION_CONVERSION_SERVICE_BEAN_NAME = "integrationConversionService";
public static final String INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME = "integrationEvaluationContext";

View File

@@ -1,4 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.integration.config.boot.IntegrationAutoConfiguration
org.springframework.integration.config.IntegrationConfigurationInitializer=\
org.springframework.integration.config.GlobalChannelInterceptorInitializer
org.springframework.integration.config.GlobalChannelInterceptorInitializer,\
org.springframework.integration.config.IntegrationConverterInitializer

View File

@@ -42,6 +42,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.GatewayHeader;
import org.springframework.integration.annotation.IntegrationComponentScan;
@@ -53,6 +55,7 @@ import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.FixedSubscriberChannel;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.channel.interceptor.WireTap;
@@ -60,8 +63,10 @@ import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.EnableMessageHistory;
import org.springframework.integration.config.EnablePublisher;
import org.springframework.integration.config.GlobalChannelInterceptor;
import org.springframework.integration.config.IntegrationConverter;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.history.MessageHistoryConfigurer;
import org.springframework.integration.message.MutableMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
@@ -69,6 +74,7 @@ import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
@@ -110,6 +116,15 @@ public class EnableIntegrationTests {
@Autowired
private AtomicInteger fbInterceptorCounter;
@Autowired
private MessageChannel numberChannel;
@Autowired
private TestConverter testConverter;
@Autowired
private MessageChannel bytesChannel;
@Test
public void testAnnotatedServiceActivator() {
this.input.send(MessageBuilder.withPayload("Foo").build());
@@ -192,6 +207,17 @@ public class EnableIntegrationTests {
child.close();
}
@Test
public void testIntegrationConverter() {
this.numberChannel.send(new GenericMessage<Integer>(10));
this.numberChannel.send(new GenericMessage<Boolean>(true));
assertThat(this.testConverter.getInvoked(), Matchers.greaterThan(0));
assertTrue(this.bytesChannel.send(new GenericMessage<byte[]>("foo".getBytes())));
assertTrue(this.bytesChannel.send(new GenericMessage<Message<?>>(new MutableMessage<Object>(""))));
}
@Configuration
@ComponentScan
@IntegrationComponentScan
@@ -271,6 +297,23 @@ public class EnableIntegrationTests {
}
@Component
@IntegrationConverter
public static class TestConverter implements Converter<Boolean, Number> {
private final AtomicInteger invoked = new AtomicInteger();
@Override
public Number convert(Boolean source) {
this.invoked.incrementAndGet();
return source ? 1 : 0;
}
public Integer getInvoked() {
return invoked.get();
}
}
@Configuration
@EnableIntegration
@ImportResource("classpath:org/springframework/integration/configuration/EnableIntegrationTests-context.xml")
@@ -293,6 +336,26 @@ public class EnableIntegrationTests {
return new DirectChannel();
}
@Bean
public QueueChannel numberChannel() {
QueueChannel channel = new QueueChannel();
channel.setDatatypes(Number.class);
return channel;
}
@Bean
public QueueChannel bytesChannel() {
QueueChannel channel = new QueueChannel();
channel.setDatatypes(byte[].class);
return channel;
}
@Bean
@IntegrationConverter
public SerializingConverter serializingConverter() {
return new SerializingConverter();
}
}
@Configuration

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,29 +16,25 @@
package org.springframework.integration.transformer;
import java.lang.reflect.Constructor;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.messaging.Message;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.util.ClassUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.springframework.messaging.Message;
/**
@@ -123,22 +119,8 @@ public class MapToObjectTransformerTests {
private BeanFactory getBeanFactory() {
GenericApplicationContext ctx = TestUtils.createTestApplicationContext();
Constructor<?> constructorToUse = null;
try {
// Add the integrationConversionService (reflection needed because of package protection)
final Class<?> conversionServiceCreatorClass = ClassUtils.forName("org.springframework.integration.context.ConversionServiceCreator",
ClassUtils.getDefaultClassLoader());
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
public Constructor<?> run() throws Exception {
return conversionServiceCreatorClass.getDeclaredConstructor((Class[]) null);
}
});
}
catch (Exception e) {
throw new RuntimeException("Unexpected Privilege Exception: ", e);
}
ctx.addBeanFactoryPostProcessor((BeanFactoryPostProcessor) BeanUtils.instantiateClass(constructorToUse));
ctx.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME,
new RootBeanDefinition("org.springframework.integration.context.CustomConversionServiceFactoryBean"));
ctx.refresh();
return ctx;
}

View File

@@ -588,10 +588,12 @@ any transaction configuration essentially allowing you to enhance the behavior o
In this scenario we need to perform type conversion. Spring Integration provides a convenient way for registering type
converters (using the Spring 3.x ConversionService) within its own instance of a conversion service bean named
<emphasis>integrationConversionService</emphasis>.
That bean is automatically created as soon as the first converter is defined using the Spring Integration namespace support.
That bean is automatically created as soon as the first converter is defined using the Spring Integration infrastructure.
To register a Converter all you need is to implement
<interfacename>org.springframework.core.convert.converter.Converter</interfacename> and define it via
<interfacename>org.springframework.core.convert.converter.Converter</interfacename>,
<interfacename>org.springframework.core.convert.converter.GenericConverter</interfacename> or
<interfacename>org.springframework.core.convert.converter.ConverterFactory</interfacename> and define it via
convenient namespace support:
<programlisting language="xml"><![CDATA[<int:converter ref="sampleConverter"/>
@@ -602,6 +604,31 @@ any transaction configuration essentially allowing you to enhance the behavior o
<bean class="o.s.i.config.xml.ConverterParserTests$TestConverter3"/>
</int:converter>]]></programlisting>
</para>
<para>
Starting with <emphasis>Spring Integration 4.0</emphasis>, the above configuration is available using annotations:
<programlisting language="java"><![CDATA[@Component
@IntegrationConverter
public class TestConverter implements Converter<Boolean, Number> {
public Number convert(Boolean source) {
return source ? 1 : 0;
}
}]]></programlisting>
or as a <code>@Configuration</code> part:
<programlisting language="java"><![CDATA[@Configuration
@EnableIntegration
public class ContextConfiguration {
@Bean
@IntegrationConverter
public SerializingConverter serializingConverter() {
return new SerializingConverter();
}
}]]></programlisting>
</para>
<important>
<para>
When configuring an <emphasis>Application Context</emphasis>, the

View File

@@ -368,6 +368,15 @@
on <classname>@Bean</classname> methods within <classname>@Configuration</classname> classes. In either case,
the bean <emphasis role="bold">must</emphasis> be a <interfacename>ChannelInterceptor</interfacename>.
</para>
<para>
The <classname>@IntegrationConverter</classname> annotation has been introduced to
mark <interfacename>Converter</interfacename>, <interfacename>GenericConverter</interfacename> or
<interfacename>ConverterFactory</interfacename> beans as candidate converters for <code>integrationConversionService</code>.
This annotation is an analogue of the <code>&lt;int:converter&gt;</code> xml element
(see <xref linkend="payload-type-conversion"/>). <code>@IntegrationConverter</code> annotations
can be placed at the class level (with a <classname>@Component</classname> stereotype annotation), or
on <classname>@Bean</classname> methods within <classname>@Configuration</classname> classes.
</para>
</section>
</chapter>

View File

@@ -56,7 +56,7 @@
<title>@MessagingGateway</title>
<para>
Messaging gateway interfaces can now be configured with the <code>@MessagingGateway</code> annotation.
It is an analogue of the <code>&lt;gateway/&gt;</code> xml element.
It is an analogue of the <code>&lt;int:gateway/&gt;</code> xml element.
For more information, see <xref linkend="messaging-gateway-annotation"/>.
</para>
</section>
@@ -68,7 +68,7 @@
Spring Integration infrastructure beans to be configured using Spring Boot's
<code>@EnableAutoConfiguration</code>.
For more information see
<ulink url="http://projects.spring.io/spring-boot/docs/spring-boot-autoconfigure/README.html"
<ulink url="http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html"
>Spring Boot - AutoConfigure</ulink>.
</para>
</section>
@@ -80,6 +80,14 @@
For more information, see <xref linkend="enable-integration"/>.
</para>
</section>
<section id="4.0-integration-converter">
<title>@IntegrationConverter</title>
<para>
The <code>@IntegrationConverter</code> annotation has bean introduced,
as an analogue of <code>&lt;int:converter/&gt;</code> component.
For more information, see <xref linkend="enable-integration"/>.
</para>
</section>
<section id="4.0-enable-publisher">
<title>@EnablePublisher</title>
<para>