INT-4225: Add MessageConverters Infrastructure

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

* To allow flexible way to convert incoming messages for the target arguments in method invocation
add `ConfigurableCompositeMessageConverter` global bean under `ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME`
* Retrieve that bean for the `MessageHandlerMethodFactor` in the `MessagingMethodInvokerHelper` to enrich afterwards target `HandlerMethodArgumentResolver`s
* The `ContentTypeConversionTests` demonstrates conversion incoming JOSN `string` into the target POJO argument in the service method

*  Add `@Primary` support
* Add documentation about `@Primary` and `contentType` conversion

INT-1800: Add MTOM Support for WS

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

The Simple WebService Inbound and Outbound Gateways can operate with `WebServiceMessage`s directly.
This allows to create those messages manually and add attachments to them.
Or, on the other hand, process incoming messages with attachments manually.

For this purpose the `SimpleWebServiceOutboundGateway` is supplied with new `extractPayload` property.
Also the `UnmarshallingTransformer` can now process `MimeMessage` as payload to unmarshal it into object graph with attachments if that

INT-4225: Add MessageConverters infrastructure

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

* To allow flexible way to convert incoming messages for the target arguments in method invocation
add `ConfigurableCompositeMessageConverter` global bean under `ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME`
* Retrieve that bean for the `MessageHandlerMethodFactor` in the `MessagingMethodInvokerHelper` to enrich afterwards target `HandlerMethodArgumentResolver`s
* The `ContentTypeConversionTests` demonstrates conversion incoming JOSN `string` into the target POJO argument in the service method

*  Add `@Primary` support
* Add documentation about `@Primary` and `contentType` conversion

Doc Polishing

Change `@Primary` to `@Default`

Doc Polishing
This commit is contained in:
Artem Bilan
2017-03-01 17:51:54 -05:00
committed by Gary Russell
parent 5fe605470b
commit 28e8f23fd0
11 changed files with 436 additions and 21 deletions

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2017 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.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that the class member has some default meaning.
* <p>
* The target parsing logic may vary.
* One of the use-cases is service with several methods which are
* selected for invocation at runtime by some condition.
* The method with this {@link Default} annotation may mean
* a fallback option when no one other method meets condition.
*
* @author Artem Bilan
*
* @since 5.0
*/
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Default {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 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.
@@ -47,8 +47,10 @@ import org.springframework.integration.config.annotation.MessagingAnnotationPost
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.converter.ConfigurableCompositeMessageConverter;
import org.springframework.integration.support.converter.DefaultDatatypeChannelMessageConverter;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.util.ClassUtils;
/**
@@ -86,19 +88,20 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
this.registerImplicitChannelCreator(registry);
this.registerIntegrationConfigurationBeanFactoryPostProcessor(registry);
this.registerIntegrationEvaluationContext(registry);
this.registerIntegrationProperties(registry);
this.registerHeaderChannelRegistry(registry);
this.registerGlobalChannelInterceptorProcessor(registry);
this.registerBuiltInBeans(registry);
this.registerDefaultConfiguringBeanFactoryPostProcessor(registry);
this.registerDefaultDatatypeChannelMessageConverter(registry);
registerImplicitChannelCreator(registry);
registerIntegrationConfigurationBeanFactoryPostProcessor(registry);
registerIntegrationEvaluationContext(registry);
registerIntegrationProperties(registry);
registerHeaderChannelRegistry(registry);
registerGlobalChannelInterceptorProcessor(registry);
registerBuiltInBeans(registry);
registerDefaultConfiguringBeanFactoryPostProcessor(registry);
registerDefaultDatatypeChannelMessageConverter(registry);
registerArgumentResolverMessageConverter(registry);
if (importingClassMetadata != null) {
this.registerMessagingAnnotationPostProcessors(importingClassMetadata, registry);
registerMessagingAnnotationPostProcessors(importingClassMetadata, registry);
}
this.registerMessageBuilderFactory(registry);
registerMessageBuilderFactory(registry);
IntegrationConfigUtils.registerRoleControllerDefinitionIfNecessary(registry);
}
@@ -405,6 +408,21 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
}
/**
* Register the default {@link CompositeMessageConverter} for argument resolvers
* during handler method invocation.
* @param registry the registry.
*/
private void registerArgumentResolverMessageConverter(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)) {
BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder
.genericBeanDefinition(ConfigurableCompositeMessageConverter.class);
registry.registerBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
postProcessorBuilder.getBeanDefinition());
}
}
private void registerMessageBuilderFactory(BeanDefinitionRegistry registry) {
boolean alreadyRegistered = false;
if (registry instanceof ListableBeanFactory) {

View File

@@ -81,8 +81,11 @@ public abstract class IntegrationContextUtils {
public static final String INTEGRATION_GRAPH_SERVER_BEAN_NAME = "integrationGraphServer";
public static final String SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME = "spelPropertyAccessorRegistrar";
public static final String ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME = "integrationArgumentResolverMessageConverter";
/**
* @param beanFactory BeanFactory for lookup, must not be null.
* @return The {@link MetadataStore} bean whose name is "metadataStore".

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2017 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.support.converter;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.integration.support.json.JacksonJsonUtils;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.GenericMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
/**
* A {@link CompositeMessageConverter} extension with some default {@link MessageConverter}s
* which can be overridden with the given converters
* or added in the end of target {@code converters} collection.
* <p>
* The default converts are (declared exactly in this order):
* <ul>
* <li> {@link MappingJackson2MessageConverter} if Jackson processor is present in classpath;
* <li> {@link ByteArrayMessageConverter}
* <li> {@link ObjectStringMessageConverter}
* <li> {@link GenericMessageConverter}
* </ul>
*
* @author Artem Bilan
*
* @since 5.0
*/
public class ConfigurableCompositeMessageConverter extends CompositeMessageConverter {
/**
* Create an instance with the default converters.
*/
public ConfigurableCompositeMessageConverter() {
super(initDefaults());
}
/**
* Create an instance with the given converters and without defaults.
* @param converters the converters to use
*/
public ConfigurableCompositeMessageConverter(Collection<MessageConverter> converters) {
this(converters, false);
}
/**
* Create an instance with the given converters and with defaults in the end.
* @param converters the converters to use
* @param registerDefaults register or not default converts
*/
public ConfigurableCompositeMessageConverter(Collection<MessageConverter> converters, boolean registerDefaults) {
super(registerDefaults ?
Stream.concat(converters.stream(), initDefaults().stream()).collect(Collectors.toList())
: converters);
}
private static Collection<MessageConverter> initDefaults() {
List<MessageConverter> converters = new LinkedList<>();
if (JacksonJsonUtils.isJackson2Present()) {
converters.add(new MappingJackson2MessageConverter());
}
converters.add(new ByteArrayMessageConverter());
converters.add(new ObjectStringMessageConverter());
// TODO do we port it together with MessageConverterUtils ?
// converters.add(new JavaSerializationMessageConverter());
converters.add(new GenericMessageConverter());
return converters;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2017 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.support.converter;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.StringMessageConverter;
/**
* A {@link StringMessageConverter} extension to convert any object to string.
* <p>
* Delegates to super when payload is {@code byte[]} or {@code String}.
* Performs {@link Object#toString()} in other cases.
*
* @author Marius Bogoevici
* @author Artem Bilan
*
* @since 5.0
*/
public class ObjectStringMessageConverter extends StringMessageConverter {
@Override
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
Object payload = message.getPayload();
if (payload instanceof String || payload instanceof byte[]) {
return super.convertFromInternal(message, targetClass, conversionHint);
}
else {
return payload.toString();
}
}
}

View File

@@ -57,8 +57,10 @@ import org.springframework.expression.Expression;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.annotation.Default;
import org.springframework.integration.annotation.Payloads;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.handler.support.CollectionArgumentResolver;
import org.springframework.integration.handler.support.MapArgumentResolver;
import org.springframework.integration.handler.support.PayloadExpressionArgumentResolver;
@@ -67,6 +69,7 @@ import org.springframework.integration.support.MutableMessage;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
@@ -158,6 +161,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
private boolean useSpelInvoker;
private HandlerMethod defaultHandlerMethod;
public MessagingMethodInvokerHelper(Object targetObject, Method method, Class<?> expectedType,
boolean canProcessMessageList) {
@@ -271,6 +275,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
InvocableHandlerMethod invocableHandlerMethod =
this.messageHandlerMethodFactory.createInvocableHandlerMethod(targetObject, method);
this.handlerMethod = new HandlerMethod(invocableHandlerMethod, canProcessMessageList);
this.defaultHandlerMethod = null;
}
catch (IneligibleMethodException e) {
throw new IllegalArgumentException(e);
@@ -399,15 +404,29 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
customArgumentResolvers.add(mapArgumentResolver);
this.messageHandlerMethodFactory.setCustomArgumentResolvers(customArgumentResolvers);
if (getBeanFactory() != null &&
getBeanFactory()
.containsBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)) {
this.messageHandlerMethodFactory
.setMessageConverter(getBeanFactory()
.getBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
MessageConverter.class));
}
this.messageHandlerMethodFactory.afterPropertiesSet();
prepareEvaluationContext();
this.initialized = true;
}
}
}
HandlerMethod candidate = this.findHandlerMethodForParameters(parameters);
Expression expression = candidate.expression;
HandlerMethod candidate = findHandlerMethodForParameters(parameters);
if (candidate == null) {
candidate = this.defaultHandlerMethod;
}
Assert.notNull(candidate, "No candidate methods found for messages.");
Expression expression = candidate.expression;
T result;
if (this.useSpelInvoker || candidate.spelOnly) {
@@ -443,8 +462,8 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
if (!(e.getCause() instanceof IllegalArgumentException) ||
!e.getStackTrace()[0].getClassName().equals(InvocableHandlerMethod.class.getName()) ||
(!"argument type mismatch".equals(e.getCause().getMessage()) &&
// JVM generates GeneratedMethodAccessor### after several calls with less error checking
!e.getCause().getMessage().startsWith("java.lang.ClassCastException@"))) {
// JVM generates GeneratedMethodAccessor### after several calls with less error checking
!e.getCause().getMessage().startsWith("java.lang.ClassCastException@"))) {
throw e;
}
}
@@ -545,6 +564,11 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
}
return;
}
if (AnnotationUtils.getAnnotation(method1, Default.class) != null) {
Assert.state(this.defaultHandlerMethod == null,
() -> "Only one method can be @Default, but there are more for: " + targetObject);
this.defaultHandlerMethod = handlerMethod1;
}
Class<?> targetParameterType = handlerMethod1.getTargetParameterType();
if (matchesAnnotation || annotationType == null) {
if (handlerMethod1.isMessageMethod()) {

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2017 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.json;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
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.Configuration;
import org.springframework.integration.annotation.Default;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.GatewayHeader;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Artem Bilan
*
* @since 5.0
*/
@RunWith(SpringRunner.class)
public class ContentTypeConversionTests {
@Autowired
private AtomicReference<Object> sendData;
@Autowired
private ServiceGateway serviceGateway;
@Test
public void testContentTypeBasedConversion() {
String json = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42}";
TestPerson testPerson = this.serviceGateway.convertJsonToPerson(json);
assertEquals(json, this.sendData.get());
assertEquals("John", testPerson.getFirstName());
assertEquals(42, testPerson.getAge());
Map<String, String> map = Collections.singletonMap("foo", "bar");
String result = this.serviceGateway.mapToString(map);
assertEquals(map.toString(), result);
}
@MessagingGateway
interface ServiceGateway {
@Gateway(headers = @GatewayHeader(name = MessageHeaders.CONTENT_TYPE, value = "application/json"))
TestPerson convertJsonToPerson(String jsonPerson);
String mapToString(Map<?, ?> map);
}
@Configuration
@EnableIntegration
public static class Config {
@Bean
public AtomicReference<Object> sendData() {
return new AtomicReference<>();
}
@Bean
public MessageChannel serviceChannel(final AtomicReference<Object> sendData) {
return MessageChannels.direct()
.interceptor(new ChannelInterceptorAdapter() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
sendData.set(message.getPayload());
return super.preSend(message, channel);
}
})
.get();
}
@Bean
public IntegrationFlow serviceFlow() {
return IntegrationFlows.from(ServiceGateway.class)
.channel("serviceChannel")
.handle(this)
.get();
}
@ServiceActivator
@Default
public TestPerson handleJson(TestPerson person) {
return person;
}
@ServiceActivator
public String handleMap(@Payload Map<?, ?> map) {
return map.toString();
}
}
}

View File

@@ -437,7 +437,7 @@ Throughout the reference manual, you will also see specific configuration and im
In the case of an Object, such a parameter will be mapped to a Message payload or part of the payload or header (when using the Spring Expression Language).
However there are times when the type of input parameter of the endpoint method does not match the type of the payload or its part.
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 _integrationConversionService_.
Spring Integration provides a convenient way for registering type converters (using the Spring `ConversionService`) within its own instance of a conversion service bean named _integrationConversionService_.
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 `org.springframework.core.convert.converter.Converter`, `org.springframework.core.convert.converter.GenericConverter` or `org.springframework.core.convert.converter.ConverterFactory`.
@@ -510,6 +510,38 @@ However, if you do want to use the Spring _conversionService_ as the Spring Inte
In this case the _conversionService_'s Converters will be available for Spring Integration runtime conversion.
=====
[[content-type-conversion]]
==== Content Type Conversion
Starting with _version 5.0_, by default, the method invocation mechanism is based on the `org.springframework.messaging.handler.invocation.InvocableHandlerMethod` infrastructure.
Its `HandlerMethodArgumentResolver` implementations (e.g. `PayloadArgumentResolver` and `MessageMethodArgumentResolver`) can use the `MessageConverter` abstraction to convert an incoming `payload` to the target method argument type.
The conversion can be based on the `contentType` message header.
For this purpose Spring Integration provides the `ConfigurableCompositeMessageConverter` that delegates to a list of registered converters to be invoked until one of them returns a non-null result.
By default this converter provides (in strict order):
* `MappingJackson2MessageConverter` if Jackson processor is present in classpath;
* `ByteArrayMessageConverter`
* `ObjectStringMessageConverter`
* `GenericMessageConverter`
Please, consult their JavaDocs for more information about their purpose and appropriate `contentType` value for conversion.
The `ConfigurableCompositeMessageConverter` is used because it can be be supplied with any other `MessageConverter` s including or excluding above mentioned default converters and registered as an appropriate bean in the application context overriding the default one:
[source,java]
----
@Bean(name = IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)
public ConfigurableCompositeMessageConverter compositeMessageConverter() {
List<MessageConverter> converters =
Arrays.asList(new MarshallingMessageConverter(jaxb2Marshaller()),
new JavaSerializationMessageConverter());
return new ConfigurableCompositeMessageConverter(converters);
}
----
And those two new converters will be registered in the composite before the defaults.
You can also not use a `ConfigurableCompositeMessageConverter`, but provide your own `MessageConverter` by registering a bean with the name `integrationArgumentResolverMessageConverter` (`IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME` constant).
NOTE: The `MessageConverter`-based (including `contentType` header) conversion isn't available when using SpEL method invocation.
In this case, only regular class to class conversion mentioned above in the <<payload-type-conversion>> is available.
[[async-polling]]
==== Asynchronous polling

View File

@@ -18,7 +18,18 @@ To create a Service Activator, use the 'service-activator' element with the 'inp
<int:service-activator input-channel="exampleChannel" ref="exampleHandler"/>
----
The configuration above assumes that "exampleHandler" either contains a single method annotated with the @ServiceActivator annotation or that it contains only one public method at all.
The configuration above selects all methods from the `exampleHandler` which meet one of the Messaging requirements:
- annotated with `@ServiceActivator`;
- is `public`;
- not `void` return if `requiresReply == true`.
The target method for invocation at runtime is selected for each request message by their `payload` type.
Or as a fallback to `Message<?>` type if such a method is present on target class.
Starting with _version 5.0_, one service method can be marked with the `@org.springframework.integration.annotation.Default` as a fallback for all non-matching cases.
This can be useful when using <<content-type-conversion>> with the target method being invoked after conversion.
To delegate to an explicitly defined method of any object, simply add the "method" attribute.
[source,xml]

View File

@@ -104,7 +104,7 @@ With this sample:
* That `EvaluationContext` instance is injected into the `ExpressionEvaluatingTransformer` bean.
To provide a SpEL Function via Java Configuration you should declare a `SpelFunctionFactoryBean` bean for each function.
To provide a SpEL Function via Java Configuration, you should declare a `SpelFunctionFactoryBean` bean for each function.
The sample above can be configured as follows:
[source,java]
@@ -168,8 +168,8 @@ Instead of configuring the factory bean above, simply add one or more of these c
With this sample, two custom `PropertyAccessor` s will be injected to the `EvaluationContext` in the order that they are declared.
To provide `PropertyAccessor` s via Java Configuration you should declare `SpelPropertyAccessorRegistrar` bean with the `spelPropertyAccessorRegistrar` (the `IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME` constant) name.
The sample above can be configured like:
To provide `PropertyAccessor` s via Java Configuration, you should declare a `SpelPropertyAccessorRegistrar` bean with the name `spelPropertyAccessorRegistrar` (`IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME` constant).
The sample above can be configured as follows:
[source,java]
----

View File

@@ -19,6 +19,12 @@ See <<mongodb-outbound-gateway>> for more information.
The new `AsyncHttpRequestExecutingMessageHandler` adds support for `AsyncRestTemplate` for outbound channel adapter and gateway.
See <<AsyncHttpRequestExecutingMessageHandler>> for more information.
==== Content Type Conversion
Now that we use the new `InvocableHandlerMethod` -based infrastructure for service method invocations, we can perform `contentType` conversion from payload to target method argument.
See <<content-type-conversion>> for more information.
[[x5.0-general]]
=== General Changes
@@ -36,6 +42,9 @@ See <<tx-handle-message-advice>> for more information.
The `SmartLifecycleRoleController` now provides methods to obtain status of endpoints in roles.
See <<endpoint-roles>> for more information.
When targeting POJO objects as message handlers, one of the service methods can now be marked with the `@Default` annotation to provide a fallback mechanism for non-matched conditions.
See <<service-activator-namespace>> for more information.
==== JMS Changes
Previously, Spring Integration JMS XML configuration used a default bean name `connectionFactory` for the JMS Connection Factory, allowing the property to be omitted from component definitions.