Skip to content

Commit ca138c0

Browse files
authored
GH-3844: Rework messaging annotation with @bean (#3877)
* GH-3844: Rework messaging annotation with @bean Fixes #3844 * Make `MessagingAnnotationPostProcessor` as a `BeanDefinitionRegistryPostProcessor` to process bean definitions as early as possible and register respective messaging components at that early phase * Make bean definitions parsing logic optional for AOT and native mode since beans have bean parsed during AOT building phase * Introduce a `BeanDefinitionPropertiesMapper` for easier mapping of the annotation attributes to the target `BeanDefinition` * Remove `@Bean`-related logic from method parsing process * Change the logic for `@Bean`-based endpoint bean names: since we don't deal with methods on the bean definition phase, then method name does not make sense. It even may mislead if we `@Bean` name is based on a method by default, so we end up with duplicated word in the target endpoint bean name. Now we don't * Fix `configuration.adoc` respectively for a new endpoint bean name logic * In the end the new logic in the `AbstractMethodAnnotationPostProcessor` is similar to XML parsers: we feed annotation attributes to the `AbstractStandardMessageHandlerFactoryBean` impls * * Fix language in docs and exception message
1 parent c1dbb02 commit ca138c0

File tree

33 files changed

+1121
-804
lines changed

33 files changed

+1121
-804
lines changed

spring-integration-amqp/src/test/java/org/springframework/integration/amqp/inbound/AmqpMessageSourceIntegrationTests.java

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,9 @@
2222
import java.util.concurrent.TimeUnit;
2323

2424
import org.aopalliance.intercept.MethodInterceptor;
25-
import org.junit.After;
26-
import org.junit.AfterClass;
27-
import org.junit.Before;
28-
import org.junit.ClassRule;
29-
import org.junit.Test;
30-
import org.junit.runner.RunWith;
25+
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
3128

3229
import org.springframework.amqp.core.Message;
3330
import org.springframework.amqp.core.Queue;
@@ -36,7 +33,7 @@
3633
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
3734
import org.springframework.amqp.rabbit.core.RabbitAdmin;
3835
import org.springframework.amqp.rabbit.core.RabbitTemplate;
39-
import org.springframework.amqp.rabbit.junit.BrokerRunning;
36+
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
4037
import org.springframework.aop.framework.ProxyFactory;
4138
import org.springframework.beans.BeansException;
4239
import org.springframework.beans.factory.annotation.Autowired;
@@ -59,39 +56,40 @@
5956
import org.springframework.messaging.handler.annotation.Header;
6057
import org.springframework.messaging.support.MessageBuilder;
6158
import org.springframework.test.annotation.DirtiesContext;
62-
import org.springframework.test.context.junit4.SpringRunner;
59+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
6360

6461
/**
6562
* @author Gary Russell
6663
*
6764
* @since 5.0.1
6865
*
6966
*/
70-
@RunWith(SpringRunner.class)
67+
@SpringJUnitConfig
7168
@DirtiesContext
69+
@RabbitAvailable({
70+
AmqpMessageSourceIntegrationTests.DSL_QUEUE,
71+
AmqpMessageSourceIntegrationTests.INTERCEPT_QUEUE,
72+
AmqpMessageSourceIntegrationTests.DLQ,
73+
AmqpMessageSourceIntegrationTests.NOAUTOACK_QUEUE })
7274
public class AmqpMessageSourceIntegrationTests {
7375

74-
private static final String DSL_QUEUE = "AmqpMessageSourceIntegrationTests";
76+
static final String DSL_QUEUE = "AmqpMessageSourceIntegrationTests";
7577

76-
private static final String QUEUE_WITH_DLQ = "AmqpMessageSourceIntegrationTests.withDLQ";
78+
static final String QUEUE_WITH_DLQ = "AmqpMessageSourceIntegrationTests.withDLQ";
7779

78-
private static final String DLQ = QUEUE_WITH_DLQ + ".dlq";
80+
static final String DLQ = QUEUE_WITH_DLQ + ".dlq";
7981

80-
private static final String INTERCEPT_QUEUE = "AmqpMessageSourceIntegrationTests.channel";
82+
static final String INTERCEPT_QUEUE = "AmqpMessageSourceIntegrationTests.channel";
8183

82-
private static final String NOAUTOACK_QUEUE = "AmqpMessageSourceIntegrationTests.noAutoAck";
83-
84-
@ClassRule
85-
public static BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues(DSL_QUEUE, INTERCEPT_QUEUE, DLQ,
86-
NOAUTOACK_QUEUE);
84+
static final String NOAUTOACK_QUEUE = "AmqpMessageSourceIntegrationTests.noAutoAck";
8785

8886
@Autowired
8987
private Config config;
9088

9189
@Autowired
9290
private ConfigurableApplicationContext context;
9391

94-
@Before
92+
@BeforeEach
9593
public void before() {
9694
RabbitAdmin admin = new RabbitAdmin(this.config.connectionFactory());
9795
Queue queue = QueueBuilder.nonDurable(QUEUE_WITH_DLQ)
@@ -103,16 +101,11 @@ public void before() {
103101
this.context.start();
104102
}
105103

106-
@After
104+
@AfterEach
107105
public void after() {
108106
this.context.stop();
109107
}
110108

111-
@AfterClass
112-
public static void afterClass() {
113-
brokerRunning.removeTestQueues(QUEUE_WITH_DLQ);
114-
}
115-
116109
@Test
117110
public void testImplicitNackThenAck() throws Exception {
118111
RabbitTemplate template = new RabbitTemplate(this.config.connectionFactory());
@@ -256,7 +249,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
256249

257250
@Bean
258251
public ConnectionFactory connectionFactory() {
259-
return new CachingConnectionFactory(brokerRunning.getConnectionFactory());
252+
return new CachingConnectionFactory("localhost");
260253
}
261254

262255
}

spring-integration-core/src/main/java/org/springframework/integration/config/IdempotentReceiverAutoProxyCreatorInitializer.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2021 the original author or authors.
2+
* Copyright 2014-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -95,8 +95,7 @@ private void annotated(ConfigurableListableBeanFactory beanFactory,
9595
List<Map<String, String>> idempotentEndpointsMapping, String beanName, BeanDefinition beanDefinition)
9696
throws LinkageError {
9797

98-
if (beanDefinition.getSource() instanceof MethodMetadata) {
99-
MethodMetadata beanMethod = (MethodMetadata) beanDefinition.getSource();
98+
if (beanDefinition.getSource() instanceof MethodMetadata beanMethod) {
10099
String annotationType = IdempotentReceiver.class.getName();
101100
if (beanMethod.isAnnotated(annotationType)) { // NOSONAR never null
102101
Object value = beanMethod.getAnnotationAttributes(annotationType).get("value"); // NOSONAR
@@ -120,13 +119,7 @@ private void annotated(ConfigurableListableBeanFactory beanFactory,
120119

121120
String endpoint = beanName;
122121
if (!MessageHandler.class.isAssignableFrom(returnType)) {
123-
/*
124-
MessageHandler beans, populated from @Bean methods, have a complex id,
125-
including @Configuration bean name, method name and the Messaging annotation name.
126-
The following pattern matches the bean name, regardless of the annotation name.
127-
*/
128-
endpoint = beanDefinition.getFactoryBeanName() + "." + beanName +
129-
".*" + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
122+
endpoint = beanName + ".*" + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
130123
}
131124

132125
String[] interceptors = (String[]) value;

spring-integration-core/src/main/java/org/springframework/integration/config/RouterFactoryBean.java

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.util.Map;
2020

2121
import org.springframework.expression.Expression;
22+
import org.springframework.integration.JavaUtils;
2223
import org.springframework.integration.context.IntegrationObjectSupport;
2324
import org.springframework.integration.handler.AbstractMessageProducingHandler;
2425
import org.springframework.integration.router.AbstractMappingMessageRouter;
@@ -49,6 +50,10 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
4950

5051
private String defaultOutputChannelName;
5152

53+
private String prefix;
54+
55+
private String suffix;
56+
5257
private Boolean resolutionRequired;
5358

5459
private Boolean applySequence;
@@ -63,6 +68,14 @@ public void setDefaultOutputChannelName(String defaultOutputChannelName) {
6368
this.defaultOutputChannelName = defaultOutputChannelName;
6469
}
6570

71+
public void setPrefix(String prefix) {
72+
this.prefix = prefix;
73+
}
74+
75+
public void setSuffix(String suffix) {
76+
this.suffix = suffix;
77+
}
78+
6679
public void setResolutionRequired(Boolean resolutionRequired) {
6780
this.resolutionRequired = resolutionRequired;
6881
}
@@ -106,7 +119,7 @@ protected MessageHandler createMethodInvokingHandler(Object targetObject, String
106119

107120
@Override
108121
protected MessageHandler createExpressionEvaluatingHandler(Expression expression) {
109-
return this.configureRouter(new ExpressionEvaluatingRouter(expression));
122+
return configureRouter(new ExpressionEvaluatingRouter(expression));
110123
}
111124

112125
protected AbstractMappingMessageRouter createMethodInvokingRouter(Object targetObject, String targetMethodName) {
@@ -116,34 +129,25 @@ protected AbstractMappingMessageRouter createMethodInvokingRouter(Object targetO
116129
}
117130

118131
protected AbstractMessageRouter configureRouter(AbstractMessageRouter router) {
119-
if (this.defaultOutputChannel != null) {
120-
router.setDefaultOutputChannel(this.defaultOutputChannel);
121-
}
122-
if (this.defaultOutputChannelName != null) {
123-
router.setDefaultOutputChannelName(this.defaultOutputChannelName);
124-
}
125-
if (getSendTimeout() != null) {
126-
router.setSendTimeout(getSendTimeout());
127-
}
128-
if (this.applySequence != null) {
129-
router.setApplySequence(this.applySequence);
130-
}
131-
if (this.ignoreSendFailures != null) {
132-
router.setIgnoreSendFailures(this.ignoreSendFailures);
133-
}
132+
JavaUtils.INSTANCE
133+
.acceptIfNotNull(this.defaultOutputChannel, router::setDefaultOutputChannel)
134+
.acceptIfNotNull(this.defaultOutputChannelName, router::setDefaultOutputChannelName)
135+
.acceptIfNotNull(getSendTimeout(), router::setSendTimeout)
136+
.acceptIfNotNull(this.applySequence, router::setApplySequence)
137+
.acceptIfNotNull(this.ignoreSendFailures, router::setIgnoreSendFailures);
138+
134139
if (router instanceof AbstractMappingMessageRouter) {
135-
this.configureMappingRouter((AbstractMappingMessageRouter) router);
140+
configureMappingRouter((AbstractMappingMessageRouter) router);
136141
}
137142
return router;
138143
}
139144

140145
protected void configureMappingRouter(AbstractMappingMessageRouter router) {
141-
if (this.channelMappings != null) {
142-
router.setChannelMappings(this.channelMappings);
143-
}
144-
if (this.resolutionRequired != null) {
145-
router.setResolutionRequired(this.resolutionRequired);
146-
}
146+
JavaUtils.INSTANCE
147+
.acceptIfNotNull(this.channelMappings, router::setChannelMappings)
148+
.acceptIfNotNull(this.resolutionRequired, router::setResolutionRequired)
149+
.acceptIfHasText(this.prefix, router::setPrefix)
150+
.acceptIfHasText(this.suffix, router::setSuffix);
147151
}
148152

149153
@Override

0 commit comments

Comments
 (0)