INT-4417: Add @EndpointId
JIRA: https://jira.spring.io/browse/INT-4417 Provide more flexibility with bean naming when using EIP Annotations with Java configuration. * Polishing - PR Comments * More polishing - remove `@Inherited`; disallow with more than one EIP annotation.
This commit is contained in:
committed by
Artem Bilan
parent
bc25eff34c
commit
212f478151
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2018 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 java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* When used alongside an EIP annotation (and no {@code @Bean}), specifies the bean name of
|
||||
* the consumer bean with the handler bean being {@code id.handler} (for a consuming
|
||||
* endpoint) or {@code id.source} for a message source (e.g. inbound channel adapter).
|
||||
* <p>
|
||||
* When there is also a {@code @Bean} annotation, this is the name of the consumer or
|
||||
* source polling bean (the handler or source gets the normal {@code @Bean} name). When
|
||||
* using on a {@code MessageHandler @Bean}, it is recommended to name the bean
|
||||
* {@code foo.handler} when using {@code @EndpointId("foo"}. This will align with
|
||||
* conventions in the framework. Similarly, for a message source, use
|
||||
* {@code @Bean("bar.source"} and {@code @EndpointId("bar")}.
|
||||
* <p>
|
||||
* <b>This is not allowed if there are multiple EIP annotations on the same method.</b>
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.0.4
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface EndpointId {
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -59,6 +59,7 @@ import org.springframework.integration.endpoint.ReactiveStreamsConsumer;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.handler.AbstractMessageProducingHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.ReplyProducingMessageHandlerWrapper;
|
||||
import org.springframework.integration.handler.advice.HandleMessageAdvice;
|
||||
import org.springframework.integration.router.AbstractMessageRouter;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
@@ -176,6 +177,10 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
|
||||
if (!handlerExists) {
|
||||
String handlerBeanName = generateHandlerBeanName(beanName, method);
|
||||
if (handler instanceof ReplyProducingMessageHandlerWrapper
|
||||
&& StringUtils.hasText(MessagingAnnotationUtils.endpointIdValue(method))) {
|
||||
handlerBeanName = handlerBeanName + ".wrapper";
|
||||
}
|
||||
this.beanFactory.registerSingleton(handlerBeanName, handler);
|
||||
handler = (MessageHandler) this.beanFactory.initializeBean(handler, handlerBeanName);
|
||||
}
|
||||
@@ -417,12 +422,15 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
}
|
||||
|
||||
protected String generateHandlerBeanName(String originalBeanName, Method method) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "."
|
||||
+ ClassUtils.getShortNameAsProperty(this.annotationType);
|
||||
String name = baseName;
|
||||
int count = 1;
|
||||
while (this.beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
String name = MessagingAnnotationUtils.endpointIdValue(method);
|
||||
if (!StringUtils.hasText(name)) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "."
|
||||
+ ClassUtils.getShortNameAsProperty(this.annotationType);
|
||||
name = baseName;
|
||||
int count = 1;
|
||||
while (this.beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
}
|
||||
}
|
||||
return name + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -55,8 +55,6 @@ import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -77,8 +75,6 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
private final Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> postProcessors =
|
||||
new HashMap<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>>();
|
||||
|
||||
private final MultiValueMap<String, String> lazyLifecycleRoles = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private final Set<Class<?>> noAnnotationsCache =
|
||||
@@ -151,7 +147,11 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(MessagingAnnotationUtils.endpointIdValue(method))
|
||||
&& annotationChains.keySet().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();
|
||||
@@ -225,7 +225,7 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
List<Annotation> annotationChain = new LinkedList<Annotation>();
|
||||
Set<Annotation> visited = new HashSet<Annotation>();
|
||||
for (Annotation ann : annotations) {
|
||||
this.recursiveFindAnnotation(annotationType, ann, annotationChain, visited);
|
||||
recursiveFindAnnotation(annotationType, ann, annotationChain, visited);
|
||||
if (annotationChain.size() > 0) {
|
||||
Collections.reverse(annotationChain);
|
||||
return annotationChain;
|
||||
@@ -244,7 +244,7 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
if (!ann.equals(metaAnn) && !visited.contains(metaAnn)
|
||||
&& !(metaAnn.annotationType().getPackage().getName().startsWith("java.lang"))) {
|
||||
visited.add(metaAnn); // prevent infinite recursion if the same annotation is found again
|
||||
if (this.recursiveFindAnnotation(annotationType, metaAnn, annotationChain, visited)) {
|
||||
if (recursiveFindAnnotation(annotationType, metaAnn, annotationChain, visited)) {
|
||||
annotationChain.add(ann);
|
||||
return true;
|
||||
}
|
||||
@@ -255,12 +255,15 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
|
||||
protected String generateBeanName(String originalBeanName, Method method,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "."
|
||||
+ ClassUtils.getShortNameAsProperty(annotationType);
|
||||
String name = baseName;
|
||||
int count = 1;
|
||||
while (this.beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
String name = MessagingAnnotationUtils.endpointIdValue(method);
|
||||
if (!StringUtils.hasText(name)) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "."
|
||||
+ ClassUtils.getShortNameAsProperty(annotationType);
|
||||
name = baseName;
|
||||
int count = 1;
|
||||
while (this.beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.integration.annotation.EndpointId;
|
||||
import org.springframework.integration.annotation.Payloads;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
@@ -117,6 +118,17 @@ public final class MessagingAnnotationUtils {
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link EndpointId#value()} property, if present.
|
||||
* @param method the methods.
|
||||
* @return the id, or null.
|
||||
* @since 5.0.4
|
||||
*/
|
||||
public static String endpointIdValue(Method method) {
|
||||
EndpointId endpointId = AnnotationUtils.findAnnotation(method, EndpointId.class);
|
||||
return endpointId != null ? endpointId.value() : null;
|
||||
}
|
||||
|
||||
private static Class<?> getTargetClass(Object targetObject) {
|
||||
Class<?> targetClass = targetObject.getClass();
|
||||
if (AopUtils.isAopProxy(targetObject)) {
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2018 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.endpoint;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.annotation.EndpointId;
|
||||
import org.springframework.integration.annotation.InboundChannelAdapter;
|
||||
import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.config.EnableIntegrationManagement;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 5.0.4
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class BeanNameTests {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
private EventDrivenConsumer eipMethod;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipMethod.handler")
|
||||
private MessageHandler eipMethodHandler;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
private EventDrivenConsumer eipBean;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipBean.handler")
|
||||
private MessageHandler eipBeanHandler;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
private EventDrivenConsumer eipBean2;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipBean2.handler")
|
||||
private MessageHandler eipBean2Handler;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter eipMethodSource;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipMethodSource.source")
|
||||
private MessageSource<?> eipMethodSourceSource;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter eipSource;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipSource.source")
|
||||
private MessageSource<?> eipSourceSource;
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@EnableIntegrationManagement
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public MeterRegistry meterRegistry() {
|
||||
return new SimpleMeterRegistry();
|
||||
}
|
||||
|
||||
@ServiceActivator(inputChannel = "channel")
|
||||
@EndpointId("eipMethod")
|
||||
public void service(String in) {
|
||||
if ("bar".equals(in)) {
|
||||
throw new RuntimeException("testErrorCount");
|
||||
}
|
||||
}
|
||||
|
||||
@Bean("eipBean.handler")
|
||||
@EndpointId("eipBean")
|
||||
@ServiceActivator(inputChannel = "channel2")
|
||||
public MessageHandler replyingHandler() {
|
||||
return new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Bean("eipBean2.handler")
|
||||
@EndpointId("eipBean2")
|
||||
@ServiceActivator(inputChannel = "channel3")
|
||||
public MessageHandler handler() {
|
||||
return m -> { };
|
||||
}
|
||||
|
||||
@EndpointId("eipMethodSource")
|
||||
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
|
||||
public String pojoSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Bean("eipSource.source")
|
||||
@EndpointId("eipSource")
|
||||
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
|
||||
public MessageSource<?> source() {
|
||||
return () -> null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.annotation.EndpointId;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.AbstractPollableChannel;
|
||||
@@ -38,7 +39,9 @@ import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.config.EnableIntegrationManagement;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.endpoint.AbstractMessageSource;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
@@ -67,6 +70,9 @@ public class MicrometerMetricsTests {
|
||||
@Autowired
|
||||
private AbstractMessageChannel channel;
|
||||
|
||||
@Autowired
|
||||
private AbstractMessageChannel channel2;
|
||||
|
||||
@Autowired
|
||||
private MessageSource<?> source;
|
||||
|
||||
@@ -90,6 +96,7 @@ public class MicrometerMetricsTests {
|
||||
catch (MessagingException e) {
|
||||
assertThat(e.getCause().getMessage()).isEqualTo("testErrorCount");
|
||||
}
|
||||
this.channel2.send(message);
|
||||
this.queue.send(message);
|
||||
this.queue.send(message);
|
||||
this.queue.receive();
|
||||
@@ -103,8 +110,8 @@ public class MicrometerMetricsTests {
|
||||
}
|
||||
nullChannel.send(message);
|
||||
MeterRegistry registry = this.meterRegistry;
|
||||
assertThat(registry.get("spring.integration.channels").gauge().value()).isEqualTo(5);
|
||||
assertThat(registry.get("spring.integration.handlers").gauge().value()).isEqualTo(2);
|
||||
assertThat(registry.get("spring.integration.channels").gauge().value()).isEqualTo(6);
|
||||
assertThat(registry.get("spring.integration.handlers").gauge().value()).isEqualTo(3);
|
||||
assertThat(registry.get("spring.integration.sources").gauge().value()).isEqualTo(1);
|
||||
|
||||
assertThat(registry.get("spring.integration.receive")
|
||||
@@ -118,7 +125,12 @@ public class MicrometerMetricsTests {
|
||||
.counter().count()).isEqualTo(1);
|
||||
|
||||
assertThat(registry.get("spring.integration.send")
|
||||
.tag("name", "micrometerMetricsTests.Config.service.serviceActivator.handler")
|
||||
.tag("name", "eipBean.handler")
|
||||
.tag("result", "success")
|
||||
.timer().count()).isEqualTo(1);
|
||||
|
||||
assertThat(registry.get("spring.integration.send")
|
||||
.tag("name", "eipMethod.handler")
|
||||
.tag("result", "success")
|
||||
.timer().count()).isEqualTo(1);
|
||||
|
||||
@@ -133,7 +145,7 @@ public class MicrometerMetricsTests {
|
||||
.timer().count()).isEqualTo(1);
|
||||
|
||||
assertThat(registry.get("spring.integration.send")
|
||||
.tag("name", "micrometerMetricsTests.Config.service.serviceActivator.handler")
|
||||
.tag("name", "eipMethod.handler")
|
||||
.tag("result", "failure")
|
||||
.timer().count()).isEqualTo(1);
|
||||
|
||||
@@ -171,12 +183,27 @@ public class MicrometerMetricsTests {
|
||||
}
|
||||
|
||||
@ServiceActivator(inputChannel = "channel")
|
||||
@EndpointId("eipMethod")
|
||||
public void service(String in) {
|
||||
if ("bar".equals(in)) {
|
||||
throw new RuntimeException("testErrorCount");
|
||||
}
|
||||
}
|
||||
|
||||
@Bean("eipBean.handler")
|
||||
@EndpointId("eipBean")
|
||||
@ServiceActivator(inputChannel = "channel2")
|
||||
public MessageHandler replyingHandler() {
|
||||
return new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageSource<?> source() {
|
||||
return new AbstractMessageSource<String>() {
|
||||
|
||||
@@ -598,6 +598,7 @@ public class MyFlowConfiguration {
|
||||
public Supplier<Message<String>> messageSupplier() {
|
||||
return () -> new GenericMessage<>("foo");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The meta-annotation rules work on `@Bean` methods as well (`@MyServiceActivator` above can be applied to a `@Bean` definition).
|
||||
@@ -613,11 +614,13 @@ This works like there is no Messaging Annotation on the `@Bean` method.
|
||||
`[configurationComponentName].[methodName].[decapitalizedAnnotationClassShortName]`.
|
||||
For example the endpoint (`SourcePollingChannelAdapter`) for the `consoleSource()` definition above gets a bean name like:
|
||||
`myFlowConfiguration.consoleSource.inboundChannelAdapter`.
|
||||
Also see <<endpoint-bean-names>>.
|
||||
|
||||
IMPORTANT: When using these annotations on `@Bean` definitions, the `inputChannel` must reference a declared bean; channels are not automatically declared in this case.
|
||||
|
||||
NOTE: With Java & Annotation configuration we can use any `@Conditional` (e.g. `@Profile`) definition on the `@Bean`
|
||||
method level, meaning to skip the bean registration by some condition reason:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
|
||||
@@ -211,6 +211,167 @@ image::images/target-endpoint.jpg[align="center", scaledwidth=100%]
|
||||
|
||||
NOTE: As discussed in <<overview-components-channel, Message Channel>> above, channels can be _Pollable_ or _Subscribable_; in this diagram, this is depicted by the "clock" symbol and the solid arrow (poll) and the dotted arrow (subscribe).
|
||||
|
||||
[[endpoint-bean-names]]
|
||||
==== Endpoint Bean Names
|
||||
|
||||
Consuming endpoints (anything with an `inputChannel`) consist of two beans, the consumer and message handler.
|
||||
The consumer has a reference to the message handler and invokes it as messages arrive.
|
||||
|
||||
When configuring with XML:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<int:service-activator id = "someService" ... />
|
||||
----
|
||||
|
||||
the bean names will be as follows:
|
||||
|
||||
- Consumer: `someService` (the `id`)
|
||||
- Handler: `someService.handler`
|
||||
|
||||
When using EIP annotations, the names depend on several factors.
|
||||
|
||||
**When Annotating POJO Methods**
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Component
|
||||
public class SomeComponent {
|
||||
|
||||
@ServiceActivator(inputChannel = ...)
|
||||
public String someMethod(...) {
|
||||
...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
the bean names will be as follows:
|
||||
|
||||
- Consumer: `someComponent.someMethod.serviceActivator`
|
||||
- Handler: `someComponent.someMethod.serviceActivator.handler`
|
||||
|
||||
Starting with _version 5.0.4_, these names can be modified using the `@EndpointId` annotation:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Component
|
||||
public class SomeComponent {
|
||||
|
||||
@EndpointId("someService")
|
||||
@ServiceActivator(inputChannel = ...)
|
||||
public String someMethod(...) {
|
||||
...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
the bean names will be as follows:
|
||||
|
||||
- Consumer: `someService`
|
||||
- Handler: `someService.handler`
|
||||
|
||||
i.e. `@EndpointId` creates names as created by the `id` attribute with XML configuration.
|
||||
|
||||
|
||||
**When Annotating @Beans**
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Configuratiom
|
||||
public class SomeConfiguration {
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = ...)
|
||||
public MessageHandler someHandler() {
|
||||
...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
the bean names will be as follows:
|
||||
|
||||
- Consumer: `someConfiguration.someHandler.serviceActivator`
|
||||
- Handler: `someHandler` (the `@Bean` name)
|
||||
|
||||
Starting with _version 5.0.4_, these names can be modified using the `@EndpointId` annotation:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Configuratiom
|
||||
public class SomeConfiguration {
|
||||
|
||||
@Bean("someService.handler")
|
||||
@EndpointId("someService")
|
||||
@ServiceActivator(inputChannel = ...)
|
||||
public MessageHandler someHandler() {
|
||||
...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
- Consumer: `someService`
|
||||
- Handler: `someService.handler`
|
||||
|
||||
i.e. `@EndpointId` creates names as created by the `id` attribute with XML configuration, as long as you use the convention of appending `.handler` to the `@Bean` name.
|
||||
|
||||
There is one special case where a third bean is created; for architectural reasons, if a `MessageHandler` `@Bean` does not define an `AbstractReplyProducingMessageHandler`, the framework wraps the provided bean in a `ReplyProducingMessageHandlerWrapper`.
|
||||
This wrapper supports request handler advice handling as well as emitting the normal 'produced no reply' debug log messages.
|
||||
Its bean name is the handler bean name plus `.wrapper` (when there is an `@EndpointId`, otherwise it's the normal generated handler name).
|
||||
|
||||
**Message Sources**
|
||||
|
||||
Similarly <<pollable-message-source, Pollable Message Sources>> create two beans, a `SourcePollingChannelAdapter` (SPCA) and a `MessageSource`.
|
||||
|
||||
When configuring with XML:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<int:inbound-channel-adapter id = "someAdapter" ... />
|
||||
----
|
||||
|
||||
the bean names will be as follows:
|
||||
|
||||
- SPCA: `someAdapter` (the `id`)
|
||||
- Handler: `someAdapter.source`
|
||||
|
||||
Using `@EndpointId` with Java configuration:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@EndpointId("someAdapter")
|
||||
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
|
||||
public String pojoSource() {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
the bean names will be as follows:
|
||||
|
||||
- SPCA: `someAdapter`
|
||||
- Handler: `someAdapter.source`
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Bean("someAdapter.source")
|
||||
@EndpointId("someAdapter")
|
||||
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
|
||||
public MessageSource<?> source() {
|
||||
return () -> {
|
||||
...
|
||||
};
|
||||
}
|
||||
----
|
||||
|
||||
the bean names will be as follows:
|
||||
|
||||
- SPCA: `someAdapter`
|
||||
- Handler: `someAdapter.source` (as long as you use the convention of appending `.source` to the `@Bean` name)
|
||||
|
||||
|
||||
[[configuration-enable-integration]]
|
||||
=== Configuration and @EnableIntegration
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ You can find a description of the pattern on the book's website at:
|
||||
|
||||
http://www.enterpriseintegrationpatterns.com/PollingConsumer.html[http://www.enterpriseintegrationpatterns.com/PollingConsumer.html]
|
||||
|
||||
[[pollable-message-source]]
|
||||
==== Pollable Message Source
|
||||
|
||||
Furthermore, in Spring Integration a second variation of the Polling Consumer pattern exists.
|
||||
|
||||
@@ -308,3 +308,9 @@ http://micrometer.io/[Micrometer] application monitoring is now supported (since
|
||||
See <<micrometer-integration>> for more information.
|
||||
|
||||
IMPORTANT: Changes were made to the Micrometer `Meters` in _version 5.0.3_ to make them more suitable for use in dimensional systems.
|
||||
|
||||
|
||||
==== @EndpointId Annotations
|
||||
|
||||
Introduced in _version 5.0.4_, this annotation provides control over bean naming when using Java configuration.
|
||||
See <<endpoint-bean-names>> for more information.
|
||||
|
||||
Reference in New Issue
Block a user