GH-2708: Make messaging annotations as repeatable

Fixes https://github.com/spring-projects/spring-integration/issues/2708

There are some requests to use the same service method for different input channels

* Make all the messaging annotations as `@Repeatable` with their
respective container annotations
* Modify `MessagingAnnotationPostProcessor` logic to deal with the mentioned repeatable
requirements
This commit is contained in:
Artem Bilan
2022-04-18 17:07:39 -04:00
committed by Gary Russell
parent f54d4b3d75
commit 97ab596841
24 changed files with 454 additions and 40 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -39,6 +40,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Aggregators.class)
public @interface Aggregator {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link Aggregator} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Aggregators {
Aggregator[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -45,6 +46,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(BridgeFromRepeatable.class)
public @interface BridgeFrom {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link BridgeFrom} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface BridgeFromRepeatable {
BridgeFrom[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -50,6 +51,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(BridgeToRepeatable.class)
public @interface BridgeTo {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link BridgeTo} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface BridgeToRepeatable {
BridgeTo[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -47,6 +48,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Filters.class)
public @interface Filter {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link Filter} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Filters {
Filter[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -53,6 +54,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(InboundChannelAdapters.class)
public @interface InboundChannelAdapter {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link InboundChannelAdapter} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface InboundChannelAdapters {
InboundChannelAdapter[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -50,6 +51,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Routers.class)
public @interface Router {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link Router} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Routers {
Router[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -48,6 +49,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(ServiceActivators.class)
public @interface ServiceActivator {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link ServiceActivator} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceActivators {
ServiceActivator[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -48,6 +49,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Splitters.class)
public @interface Splitter {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link Splitter} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Splitters {
Splitter[] value();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -36,6 +37,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Transformers.class)
public @interface Transformer {
/**

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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
*
* https://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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The repeatable container for {@link Transformer} annotations.
*
* @author Artem Bilan
*
* @since 6.0
*/
@Documented
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Transformers {
Transformer[] value();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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,7 +25,6 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -42,13 +41,13 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.integration.annotation.Aggregator;
import org.springframework.integration.annotation.BridgeFrom;
import org.springframework.integration.annotation.BridgeTo;
import org.springframework.integration.annotation.EndpointId;
import org.springframework.integration.annotation.Filter;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Role;
@@ -161,28 +160,34 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
}
private void doWithMethod(Method method, Object bean, String beanName, Class<?> beanClass) {
Map<Class<? extends Annotation>, List<Annotation>> annotationChains = new HashMap<>();
for (Class<? extends Annotation> annotationType :
this.postProcessors.keySet()) {
if (AnnotatedElementUtils.isAnnotated(method, annotationType.getName())) {
List<Annotation> annotationChain = getAnnotationChain(method, annotationType);
if (annotationChain.size() > 0) {
annotationChains.put(annotationType, annotationChain);
}
}
MergedAnnotations mergedAnnotations =
MergedAnnotations.search(MergedAnnotations.SearchStrategy.DIRECT)
.from(method);
List<MessagingMetaAnnotation> messagingAnnotations = new ArrayList<>();
for (Class<? extends Annotation> annotationType : this.postProcessors.keySet()) {
mergedAnnotations.stream()
.filter((ann) -> ann.getType().equals(annotationType))
.map(MergedAnnotation::getRoot)
.map(MergedAnnotation::synthesize)
.map((ann) -> new MessagingMetaAnnotation(ann, annotationType))
.forEach(messagingAnnotations::add);
}
if (StringUtils.hasText(MessagingAnnotationUtils.endpointIdValue(method))
&& annotationChains.keySet().size() > 1) {
if (mergedAnnotations.get(EndpointId.class, (ann) -> ann.hasNonDefaultValue("value")).isPresent()
&& messagingAnnotations.size() > 1) {
throw new IllegalStateException("@EndpointId on " + method.toGenericString()
+ " can only have one EIP annotation, found: " + annotationChains.keySet().size());
}
for (Entry<Class<? extends Annotation>, List<Annotation>> entry : annotationChains.entrySet()) {
Class<? extends Annotation> annotationType = entry.getKey();
List<Annotation> annotations = entry.getValue();
processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotations);
+ " can only have one EIP annotation, found: " + messagingAnnotations.size());
}
if (annotationChains.size() == 0) {
for (MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) {
Class<? extends Annotation> annotationType = messagingAnnotation.messagingAnnotationType;
List<Annotation> annotationChain = getAnnotationChain(messagingAnnotation.annotation, annotationType);
processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotationChain);
}
if (messagingAnnotations.size() == 0) {
this.noAnnotationsCache.add(beanClass);
}
}
@@ -226,10 +231,8 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
Object result = postProcessor.postProcess(bean, beanName, targetMethod, annotations);
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
BeanDefinitionRegistry definitionRegistry = (BeanDefinitionRegistry) beanFactory;
if (result instanceof AbstractEndpoint) {
AbstractEndpoint endpoint = (AbstractEndpoint) result;
String autoStartup = MessagingAnnotationUtils.resolveAttribute(annotations, "autoStartup",
String.class);
if (result instanceof AbstractEndpoint endpoint) {
String autoStartup = MessagingAnnotationUtils.resolveAttribute(annotations, "autoStartup", String.class);
if (StringUtils.hasText(autoStartup)) {
autoStartup = beanFactory.resolveEmbeddedValue(autoStartup);
if (StringUtils.hasText(autoStartup)) {
@@ -259,20 +262,19 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
}
/**
* @param method the method.
* @param messagingAnnotation the {@link Annotation} to take a chain for its meta-annotations.
* @param annotationType the annotation type.
* @return the hierarchical list of annotations in top-bottom order.
*/
protected List<Annotation> getAnnotationChain(Method method, Class<? extends Annotation> annotationType) {
protected List<Annotation> getAnnotationChain(Annotation messagingAnnotation,
Class<? extends Annotation> annotationType) {
List<Annotation> annotationChain = new LinkedList<>();
Set<Annotation> visited = new HashSet<>();
for (MergedAnnotation<Annotation> mergedAnnotation : MergedAnnotations.from(method)) {
recursiveFindAnnotation(annotationType, mergedAnnotation.synthesize(), annotationChain, visited);
if (annotationChain.size() > 0) {
Collections.reverse(annotationChain);
return annotationChain;
}
recursiveFindAnnotation(annotationType, messagingAnnotation, annotationChain, visited);
if (annotationChain.size() > 0) {
Collections.reverse(annotationChain);
}
return annotationChain;
@@ -280,6 +282,7 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
protected boolean recursiveFindAnnotation(Class<? extends Annotation> annotationType, Annotation ann,
List<Annotation> annotationChain, Set<Annotation> visited) {
if (ann.annotationType().equals(annotationType)) {
annotationChain.add(ann);
return true;
@@ -299,6 +302,7 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
protected String generateBeanName(String originalBeanName, Method method,
Class<? extends Annotation> annotationType) {
String name = MessagingAnnotationUtils.endpointIdValue(method);
if (!StringUtils.hasText(name)) {
String baseName = originalBeanName + "." + method.getName() + "."
@@ -316,4 +320,8 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
return this.postProcessors;
}
private record MessagingMetaAnnotation(Annotation annotation, Class<? extends Annotation> messagingAnnotationType) {
}
}

View File

@@ -298,6 +298,12 @@ public class MessagingAnnotationPostProcessorTests {
inputChannel.send(new GenericMessage<>("foo"));
Message<?> reply = outputChannel.receive(0);
assertThat(reply.getPayload()).isEqualTo("FOO");
MessageChannel inputChannel2 = context.getBean("inputChannel2", MessageChannel.class);
inputChannel2.send(new GenericMessage<>("test2"));
reply = outputChannel.receive(0);
assertThat(reply.getPayload()).isEqualTo("TEST2");
context.close();
}
@@ -386,6 +392,7 @@ public class MessagingAnnotationPostProcessorTests {
public static class TransformerAnnotationTestBean {
@Transformer(inputChannel = "inputChannel", outputChannel = "outputChannel")
@Transformer(inputChannel = "inputChannel2", outputChannel = "outputChannel")
public String transformBefore(String input) {
return input.toUpperCase();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 the original author or authors.
* Copyright 2014-2022 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.
@@ -385,9 +385,9 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Splitter(inputChannel = "skippedChannel2")
@ServiceActivator(inputChannel = "skippedChannel2")
@Router(inputChannel = "skippedChannel3")
@Transformer(inputChannel = "skippedChannel4")
@Filter(inputChannel = "skippedChannel4")
@Filter(inputChannel = "skippedChannel5")
@Profile("foo")
public MessageHandler skippedMessageHandler() {
@@ -396,6 +396,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
}
@Bean
@BridgeFrom("skippedChannel5")
@BridgeFrom("skippedChannel6")
@Profile("foo")
public MessageChannel skippedChannel1() {

View File

@@ -250,6 +250,9 @@ public class EnableIntegrationTests {
@Autowired
private PollableChannel messageChannel;
@Autowired
private PollableChannel messageChannel2;
@Autowired
private PollableChannel fooChannel;
@@ -432,6 +435,12 @@ public class EnableIntegrationTests {
assertThat(message.getHeaders().containsKey("foo")).isTrue();
assertThat(message.getHeaders().get("foo")).isEqualTo("FOO");
message = this.messageChannel2.receive(10_000);
assertThat(message).isNotNull();
assertThat(message.getPayload()).isEqualTo("bar");
assertThat(message.getHeaders().containsKey("foo")).isTrue();
assertThat(message.getHeaders().get("foo")).isEqualTo("FOO");
MessagingTemplate messagingTemplate = new MessagingTemplate(this.controlBusChannel);
assertThat(messagingTemplate.convertSendAndReceive("@pausable.isRunning()", Boolean.class)).isEqualTo(false);
this.controlBusChannel.send(new GenericMessage<>("@pausable.start()"));
@@ -1162,6 +1171,11 @@ public class EnableIntegrationTests {
return new QueueChannel();
}
@Bean
public PollableChannel messageChannel2() {
return new QueueChannel();
}
@Bean
public QueueChannel numberChannel() {
QueueChannel channel = new QueueChannel();
@@ -1354,6 +1368,7 @@ public class EnableIntegrationTests {
@InboundChannelAdapter(value = "messageChannel", poller = @Poller(fixedDelay = "${poller.interval}",
maxMessagesPerPoll = "1"))
@InboundChannelAdapter(value = "messageChannel2", poller = @Poller(fixedDelay = "100"))
public Message<?> message() {
return MessageBuilder.withPayload("bar").setHeader("foo", "FOO").build();
}

View File

@@ -301,7 +301,7 @@ public class ThingService {
----
====
You can also use the `@Headers` annotation to provide all of the message headers as a `Map`, as the following example shows:
You can also use the `@Headers` annotation to provide all the message headers as a `Map`, as the following example shows:
====
[source,java]
@@ -354,7 +354,7 @@ The `MessageHandler` instances (`MessageSource` instances) are also eligible to
Starting with version 4.0, all messaging annotations provide `SmartLifecycle` options (`autoStartup` and `phase`) to allow endpoint lifecycle control on application context initialization.
They default to `true` and `0`, respectively.
To change the state of an endpoint (such as ` start()` or `stop()`), you can obtain a reference to the endpoint bean by using the `BeanFactory` (or autowiring) and invoke the methods.
To change the state of an endpoint (such as `start()` or `stop()`), you can obtain a reference to the endpoint bean by using the `BeanFactory` (or autowiring) and invoke the methods.
Alternatively, you can send a command message to the `Control Bus` (see <<./control-bus.adoc#control-bus,Control Bus>>).
For these purposes, you should use the `beanName` mentioned earlier in the preceding paragraph.
@@ -380,6 +380,18 @@ Thing1 dependsOnSPCA(@Qualifier("someInboundAdapter") @Lazy SourcePollingChannel
====
=====
Starting with version 6.0, all the messaging annotations are `@Repeatable` now, so several of the same type can be declared on the same service method with the meaning to create as many endpoints as those annotations are repeated:
====
[source, java]
----
@Transformer(inputChannel = "inputChannel1", outputChannel = "outputChannel1")
@Transformer(inputChannel = "inputChannel2", outputChannel = "outputChannel2")
public String transform(String input) {
return input.toUpperCase();
}
----
====
[[configuration-using-poller-annotation]]
==== Using the `@Poller` Annotation

View File

@@ -26,8 +26,10 @@ See <<./graphql.adoc#graphql,GraphQL Support>> for more information.
[[x6.0-general]]
=== General Changes
The messaging annotations don't require a poller attribute as an array of @Poller any more.
See <<./configuration.adoc#configuration-using-poller-annotation,Using the @Poller Annotation>> for more information.
The messaging annotations are now `@Repeatable` and the same type can be declared several times on the same service method.
The messaging annotations don't require a `poller` attribute as an array of `@Poller` anymore.
See <<./configuration.adoc#annotations,Annotation Support>> for more information.
[[x6.0-http]]
=== HTTP Changes