Skip to content

Commit 9780a6c

Browse files
artembilangaryrussell
authored andcommitted
Use MergedAnnotations in MethodAnnPublisher (#2914)
* For better performance and consistency use a `MergedAnnotations` in the `MethodAnnotationPublisherMetadataSource` instead of `AnnotatedElementUtils` * Remove deprecated API from the `PublisherMetadataSource` hierarchy
1 parent 532941c commit 9780a6c

File tree

5 files changed

+91
-206
lines changed

5 files changed

+91
-206
lines changed

spring-integration-core/src/main/java/org/springframework/integration/aop/MethodAnnotationPublisherMetadataSource.java

Lines changed: 83 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,21 @@
1717
package org.springframework.integration.aop;
1818

1919
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.AnnotatedElement;
2021
import java.lang.reflect.Method;
2122
import java.util.Collections;
2223
import java.util.HashMap;
2324
import java.util.Map;
2425
import java.util.Set;
2526
import java.util.concurrent.ConcurrentHashMap;
26-
import java.util.stream.Collectors;
2727

2828
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
2929
import org.springframework.core.ParameterNameDiscoverer;
30-
import org.springframework.core.annotation.AnnotatedElementUtils;
31-
import org.springframework.core.annotation.AnnotationUtils;
30+
import org.springframework.core.annotation.MergedAnnotation;
31+
import org.springframework.core.annotation.MergedAnnotations;
3232
import org.springframework.expression.Expression;
3333
import org.springframework.integration.annotation.Publisher;
34+
import org.springframework.lang.Nullable;
3435
import org.springframework.messaging.handler.annotation.Header;
3536
import org.springframework.messaging.handler.annotation.Payload;
3637
import org.springframework.util.Assert;
@@ -80,143 +81,105 @@ public void setChannelAttributeName(String channelAttributeName) {
8081

8182
@Override
8283
public String getChannelName(Method method) {
83-
return this.channels.computeIfAbsent(method, method1 -> {
84-
String channelName = getAnnotationValue(method, this.channelAttributeName, String.class);
85-
if (channelName == null) {
86-
channelName = getAnnotationValue(method.getDeclaringClass(), this.channelAttributeName, String.class);
87-
}
88-
return StringUtils.hasText(channelName) ? channelName : null;
89-
});
90-
84+
return this.channels.computeIfAbsent(method,
85+
method1 -> {
86+
String channelName = getAnnotationValue(method, this.channelAttributeName);
87+
if (channelName == null) {
88+
channelName = getAnnotationValue(method.getDeclaringClass(), this.channelAttributeName);
89+
}
90+
return StringUtils.hasText(channelName) ? channelName : null;
91+
});
9192
}
9293

9394
@Override
9495
public Expression getExpressionForPayload(Method method) {
95-
return this.payloadExpressions.computeIfAbsent(method, method1 -> {
96-
Expression payloadExpression = null;
97-
Annotation methodPayloadAnnotation = AnnotationUtils.findAnnotation(method, Payload.class);
98-
99-
if (methodPayloadAnnotation != null) {
100-
String payloadExpressionString = getAnnotationValue(methodPayloadAnnotation, null, String.class);
101-
if (!StringUtils.hasText(payloadExpressionString)) {
102-
payloadExpression = RETURN_VALUE_EXPRESSION;
103-
}
104-
else {
105-
payloadExpression = EXPRESSION_PARSER.parseExpression(payloadExpressionString);
106-
}
107-
}
96+
return this.payloadExpressions.computeIfAbsent(method,
97+
method1 -> {
98+
Expression payloadExpression = null;
99+
MergedAnnotation<Payload> payloadMergedAnnotation =
100+
MergedAnnotations.from(method, MergedAnnotations.SearchStrategy.EXHAUSTIVE)
101+
.get(Payload.class);
102+
if (payloadMergedAnnotation.isPresent()) {
103+
String payloadExpressionString = payloadMergedAnnotation.getString("expression");
104+
if (!StringUtils.hasText(payloadExpressionString)) {
105+
payloadExpression = RETURN_VALUE_EXPRESSION;
106+
}
107+
else {
108+
payloadExpression = EXPRESSION_PARSER.parseExpression(payloadExpressionString);
109+
}
110+
}
108111

109-
Annotation[][] annotationArray = method.getParameterAnnotations();
110-
for (int i = 0; i < annotationArray.length; i++) {
111-
Annotation[] parameterAnnotations = annotationArray[i];
112-
for (Annotation currentAnnotation : parameterAnnotations) {
113-
if (Payload.class.equals(currentAnnotation.annotationType())) {
114-
Assert.state(payloadExpression == null,
115-
"@Payload can be used at most once on a @Publisher method, " +
116-
"either at method-level or on a single parameter");
112+
Annotation[][] annotationArray = method.getParameterAnnotations();
113+
for (int i = 0; i < annotationArray.length; i++) {
114+
Annotation[] parameterAnnotations = annotationArray[i];
115+
payloadMergedAnnotation = MergedAnnotations.from(parameterAnnotations).get(Payload.class);
116+
if (payloadMergedAnnotation.isPresent()) {
117+
Assert.state(payloadExpression == null,
118+
"@Payload can be used at most once on a @Publisher method, " +
119+
"either at method-level or on a single parameter");
117120

118-
Assert.state("".equals(AnnotationUtils.getValue(AnnotationUtils.synthesizeAnnotation(currentAnnotation, null))),
119-
"@Payload on a parameter for a @Publisher method may not contain an expression");
121+
Assert.state("".equals(payloadMergedAnnotation.getString("expression")),
122+
"@Payload on a parameter for a @Publisher method may not contain an 'expression'");
120123

121-
payloadExpression =
122-
EXPRESSION_PARSER.parseExpression("#" + ARGUMENT_MAP_VARIABLE_NAME + "[" + i + "]");
124+
payloadExpression =
125+
EXPRESSION_PARSER.parseExpression("#" + ARGUMENT_MAP_VARIABLE_NAME + "[" + i + "]");
126+
}
123127
}
124-
}
125-
}
126-
if (payloadExpression == null ||
127-
RETURN_VALUE_EXPRESSION.getExpressionString().equals(payloadExpression.getExpressionString())) {
128-
Assert.isTrue(!void.class.equals(method.getReturnType()),
129-
"When defining @Publisher on a void-returning method, an explicit payload " +
130-
"expression that does not rely upon a #return value is required.");
131-
}
132-
return payloadExpression;
133-
});
134-
}
135-
136-
@Override
137-
@Deprecated
138-
public String getPayloadExpression(Method method) {
139-
return getExpressionForPayload(method)
140-
.getExpressionString();
128+
if (payloadExpression == null ||
129+
RETURN_VALUE_EXPRESSION.getExpressionString()
130+
.equals(payloadExpression.getExpressionString())) {
131+
Assert.isTrue(!void.class.equals(method.getReturnType()),
132+
"When defining @Publisher on a void-returning method, an explicit payload " +
133+
"expression that does not rely upon a #return value is required.");
134+
}
135+
return payloadExpression;
136+
});
141137
}
142138

143139
@Override
144140
public Map<String, Expression> getExpressionsForHeaders(Method method) {
145-
return this.headersExpressions.computeIfAbsent(method, method1 -> {
146-
Map<String, Expression> headerExpressions = new HashMap<>();
147-
String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method);
148-
Annotation[][] annotationArray = method.getParameterAnnotations();
149-
for (int i = 0; i < annotationArray.length; i++) {
150-
Annotation[] parameterAnnotations = annotationArray[i];
151-
for (Annotation currentAnnotation : parameterAnnotations) {
152-
if (Header.class.equals(currentAnnotation.annotationType())) {
153-
String name = getAnnotationValue(currentAnnotation, null, String.class);
154-
if (!StringUtils.hasText(name)) {
155-
name = parameterNames[i];
141+
return this.headersExpressions.computeIfAbsent(method,
142+
method1 -> {
143+
Map<String, Expression> headerExpressions = new HashMap<>();
144+
String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method);
145+
Annotation[][] annotationArray = method.getParameterAnnotations();
146+
for (int i = 0; i < annotationArray.length; i++) {
147+
Annotation[] parameterAnnotations = annotationArray[i];
148+
MergedAnnotation<Header> headerMergedAnnotation =
149+
MergedAnnotations.from(parameterAnnotations).get(Header.class);
150+
if (headerMergedAnnotation.isPresent()) {
151+
String name =
152+
headerMergedAnnotation
153+
.getString("name");
154+
if (!StringUtils.hasText(name)) {
155+
if (parameterNames != null) {
156+
name = parameterNames[i];
157+
}
158+
else {
159+
name = method.getName() + ".arg#" + i;
160+
}
161+
}
162+
headerExpressions.put(name,
163+
EXPRESSION_PARSER.parseExpression('#' + ARGUMENT_MAP_VARIABLE_NAME + '[' + i + ']'));
156164
}
157-
headerExpressions.put(name,
158-
EXPRESSION_PARSER.parseExpression("#" + ARGUMENT_MAP_VARIABLE_NAME + "[" + i + "]"));
159165
}
160-
}
161-
}
162-
return headerExpressions;
163-
});
166+
return headerExpressions;
167+
});
164168
}
165169

166-
@Override
167-
@Deprecated
168-
public Map<String, String> getHeaderExpressions(Method method) {
169-
return getExpressionsForHeaders(method)
170-
.entrySet()
171-
.stream()
172-
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getExpressionString()));
173-
}
174-
175-
private <T> T getAnnotationValue(Method method, String attributeName, Class<T> expectedType) {
176-
T value = null;
170+
@Nullable
171+
private String getAnnotationValue(AnnotatedElement element, String attributeName) {
172+
MergedAnnotations mergedAnnotations =
173+
MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.EXHAUSTIVE);
174+
String value = null;
177175
for (Class<? extends Annotation> annotationType : this.annotationTypes) {
178-
Annotation annotation = AnnotatedElementUtils.findMergedAnnotation(method, annotationType);
179-
if (annotation != null) {
176+
MergedAnnotation<? extends Annotation> mergedAnnotation = mergedAnnotations.get(annotationType);
177+
if (mergedAnnotation.isPresent()) {
180178
if (value != null) {
181179
throw new IllegalStateException(
182-
"method [" + method + "] contains more than one publisher annotation");
180+
"The [" + element + "] contains more than one publisher annotation");
183181
}
184-
value = getAnnotationValue(annotation, attributeName, expectedType);
185-
}
186-
}
187-
return value;
188-
}
189-
190-
private <T> T getAnnotationValue(Class<?> clazz, String attributeName, Class<T> expectedType) {
191-
T value = null;
192-
for (Class<? extends Annotation> annotationType : this.annotationTypes) {
193-
Annotation annotation = AnnotatedElementUtils.findMergedAnnotation(clazz, annotationType);
194-
if (annotation != null) {
195-
if (value != null) {
196-
throw new IllegalStateException(
197-
"class [" + clazz + "] contains more than one publisher annotation");
198-
}
199-
value = getAnnotationValue(annotation, attributeName, expectedType);
200-
}
201-
}
202-
return value;
203-
}
204-
205-
@SuppressWarnings("unchecked")
206-
private <T> T getAnnotationValue(Annotation annotation, String attributeName, Class<T> expectedType) {
207-
T value = null;
208-
Object valueAsObject = (attributeName == null) ?
209-
AnnotationUtils.getValue(AnnotationUtils.synthesizeAnnotation(annotation, null)) :
210-
AnnotationUtils.getValue(annotation, attributeName);
211-
212-
if (valueAsObject != null) {
213-
if (expectedType.isAssignableFrom(valueAsObject.getClass())) {
214-
value = (T) valueAsObject;
215-
}
216-
else {
217-
throw new IllegalArgumentException("expected type [" + expectedType.getName() +
218-
"] for attribute '" + attributeName + "' on publisher annotation [" +
219-
annotation.annotationType() + "], but actual type was [" + valueAsObject.getClass() + "]");
182+
value = mergedAnnotation.getString(attributeName);
220183
}
221184
}
222185
return value;

spring-integration-core/src/main/java/org/springframework/integration/aop/MethodNameMappingPublisherMetadataSource.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,6 @@ public Expression getExpressionForPayload(Method method) {
7777
return null;
7878
}
7979

80-
@Override
81-
@Deprecated
82-
public String getPayloadExpression(Method method) {
83-
Expression expressionForPayload = getExpressionForPayload(method);
84-
return expressionForPayload != null ? expressionForPayload.getExpressionString() : null;
85-
}
86-
87-
@Override
88-
@Deprecated
89-
public Map<String, String> getHeaderExpressions(Method method) {
90-
Map<String, Expression> expressionsForHeaders = getExpressionsForHeaders(method);
91-
return expressionsForHeaders == null
92-
? null
93-
: expressionsForHeaders.entrySet()
94-
.stream()
95-
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getExpressionString()));
96-
}
97-
9880
@Override
9981
public Map<String, Expression> getExpressionsForHeaders(Method method) {
10082
return this.headerExpressionMap

spring-integration-core/src/main/java/org/springframework/integration/aop/PublisherMetadataSource.java

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.lang.reflect.Method;
2020
import java.util.Map;
21-
import java.util.stream.Collectors;
2221

2322
import org.springframework.expression.Expression;
2423
import org.springframework.expression.ExpressionParser;
@@ -59,38 +58,14 @@ interface PublisherMetadataSource {
5958
*/
6059
String getChannelName(Method method);
6160

62-
/**
63-
* Returns the expression string to be evaluated for creating the Message
64-
* payload.
65-
* @param method The Method.
66-
* @return The payload expression.
67-
* @deprecated since 5.0.4 in favor of {@link #getExpressionForPayload(Method)}
68-
*/
69-
@Deprecated
70-
String getPayloadExpression(Method method);
71-
7261
/**
7362
* Returns the SpEL expression to be evaluated for creating the Message
7463
* payload.
7564
* @param method the Method.
7665
* @return rhe payload expression.
7766
* @since 5.0.4
7867
*/
79-
@SuppressWarnings("deprecation")
80-
default Expression getExpressionForPayload(Method method) {
81-
return EXPRESSION_PARSER.parseExpression(getPayloadExpression(method));
82-
}
83-
84-
/**
85-
* Returns the map of expression strings to be evaluated for any headers
86-
* that should be set on the published Message. The keys in the Map are
87-
* header names, the values are the expression strings.
88-
* @param method The Method.
89-
* @return The header expressions.
90-
* @deprecated since 5.0.4 in favor of {@link #getExpressionsForHeaders(Method)}
91-
*/
92-
@Deprecated
93-
Map<String, String> getHeaderExpressions(Method method);
68+
Expression getExpressionForPayload(Method method);
9469

9570
/**
9671
* Returns the map of expression strings to be evaluated for any headers
@@ -99,13 +74,6 @@ default Expression getExpressionForPayload(Method method) {
9974
* @param method The Method.
10075
* @return The header expressions.
10176
*/
102-
@SuppressWarnings("deprecation")
103-
default Map<String, Expression> getExpressionsForHeaders(Method method) {
104-
return getHeaderExpressions(method)
105-
.entrySet()
106-
.stream()
107-
.collect(Collectors.toMap(Map.Entry::getKey,
108-
e -> EXPRESSION_PARSER.parseExpression(e.getValue())));
109-
}
77+
Map<String, Expression> getExpressionsForHeaders(Method method);
11078

11179
}

spring-integration-core/src/main/java/org/springframework/integration/aop/SimplePublisherMetadataSource.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,6 @@ public void setPayloadExpression(String payloadExpression) {
5454
this.payloadExpression = EXPRESSION_PARSER.parseExpression(payloadExpression);
5555
}
5656

57-
@Override
58-
@Deprecated
59-
public String getPayloadExpression(Method method) {
60-
return this.payloadExpression.getExpressionString();
61-
}
62-
6357
@Override
6458
public Expression getExpressionForPayload(Method method) {
6559
return this.payloadExpression;
@@ -73,16 +67,6 @@ public void setHeaderExpressions(Map<String, String> headerExpressions) {
7367
e -> EXPRESSION_PARSER.parseExpression(e.getValue())));
7468
}
7569

76-
@Override
77-
@Deprecated
78-
public Map<String, String> getHeaderExpressions(Method method) {
79-
return this.headerExpressions
80-
.entrySet()
81-
.stream()
82-
.collect(Collectors.toMap(Map.Entry::getKey,
83-
e -> e.getValue().getExpressionString()));
84-
}
85-
8670
@Override
8771
public Map<String, Expression> getExpressionsForHeaders(Method method) {
8872
return this.headerExpressions;

0 commit comments

Comments
 (0)