Skip to content

Commit bcb6f13

Browse files
committed
Add native image support for STOMP messaging
This commit adds reflection hints for messaging annotations as well as for classes and methods annotated with @MessageMapping. Closes gh-28754
1 parent 255a52b commit bcb6f13

File tree

6 files changed

+376
-0
lines changed

6 files changed

+376
-0
lines changed

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.lang.annotation.RetentionPolicy;
2323
import java.lang.annotation.Target;
2424

25+
import org.springframework.aot.hint.annotation.Reflective;
2526
import org.springframework.messaging.Message;
2627

2728
/**
@@ -106,6 +107,7 @@
106107
@Target({ElementType.TYPE, ElementType.METHOD})
107108
@Retention(RetentionPolicy.RUNTIME)
108109
@Documented
110+
@Reflective(MessageMappingReflectiveProcessor.class)
109111
public @interface MessageMapping {
110112

111113
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.messaging.handler.annotation;
18+
19+
import java.lang.reflect.AnnotatedElement;
20+
import java.lang.reflect.Method;
21+
import java.lang.reflect.Parameter;
22+
import java.lang.reflect.Type;
23+
import java.security.Principal;
24+
25+
import org.springframework.aot.hint.ExecutableMode;
26+
import org.springframework.aot.hint.ReflectionHints;
27+
import org.springframework.aot.hint.annotation.ReflectiveProcessor;
28+
import org.springframework.context.aot.BindingReflectionHintsRegistrar;
29+
import org.springframework.core.MethodParameter;
30+
import org.springframework.lang.Nullable;
31+
import org.springframework.messaging.Message;
32+
import org.springframework.messaging.MessageHeaders;
33+
import org.springframework.messaging.support.MessageHeaderAccessor;
34+
35+
/**
36+
* {@link ReflectiveProcessor} implementation for {@link MessageMapping}
37+
* annotated types. On top of registering reflection hints for invoking
38+
* the annotated method, this implementation handles:
39+
* <ul>
40+
* <li>Return types.</li>
41+
* <li>Parameters identified as potential payload.</li>
42+
* <li>{@link Message} parameters.</li>
43+
* </ul>
44+
*
45+
* @author Sebastien Deleuze
46+
* @since 6.0
47+
*/
48+
class MessageMappingReflectiveProcessor implements ReflectiveProcessor {
49+
50+
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
51+
52+
@Override
53+
public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) {
54+
if (element instanceof Class<?> type) {
55+
registerTypeHints(hints, type);
56+
}
57+
else if (element instanceof Method method) {
58+
registerMethodHints(hints, method);
59+
}
60+
}
61+
62+
protected void registerTypeHints(ReflectionHints hints, Class<?> type) {
63+
hints.registerType(type, hint -> {});
64+
}
65+
66+
protected void registerMethodHints(ReflectionHints hints, Method method) {
67+
hints.registerMethod(method, hint -> hint.setModes(ExecutableMode.INVOKE));
68+
registerParameterHints(hints, method);
69+
registerReturnValueHints(hints, method);
70+
}
71+
72+
protected void registerParameterHints(ReflectionHints hints, Method method) {
73+
hints.registerMethod(method, hint -> hint.setModes(ExecutableMode.INVOKE));
74+
for (Parameter parameter : method.getParameters()) {
75+
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
76+
if (Message.class.isAssignableFrom(methodParameter.getParameterType())) {
77+
this.bindingRegistrar.registerReflectionHints(hints, getMessageType(methodParameter));
78+
}
79+
else if (couldBePayload(methodParameter)) {
80+
this.bindingRegistrar.registerReflectionHints(hints, methodParameter.getGenericParameterType());
81+
}
82+
}
83+
}
84+
85+
protected boolean couldBePayload(MethodParameter methodParameter) {
86+
return !methodParameter.hasParameterAnnotation(DestinationVariable.class) &&
87+
!methodParameter.hasParameterAnnotation(Header.class) &&
88+
!methodParameter.hasParameterAnnotation(Headers.class) &&
89+
!MessageHeaders.class.isAssignableFrom(methodParameter.getParameterType()) &&
90+
!MessageHeaderAccessor.class.isAssignableFrom(methodParameter.getParameterType()) &&
91+
!Principal.class.isAssignableFrom(methodParameter.nestedIfOptional().getNestedParameterType());
92+
}
93+
94+
protected void registerReturnValueHints(ReflectionHints hints, Method method) {
95+
MethodParameter returnType = MethodParameter.forExecutable(method, -1);
96+
this.bindingRegistrar.registerReflectionHints(hints, returnType.getGenericParameterType());
97+
}
98+
99+
@Nullable
100+
protected Type getMessageType(MethodParameter parameter) {
101+
MethodParameter nestedParameter = parameter.nested();
102+
return (nestedParameter.getNestedParameterType() == nestedParameter.getParameterType() ? null : nestedParameter.getNestedParameterType());
103+
}
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.messaging.handler.annotation;
18+
19+
import java.util.stream.Stream;
20+
21+
import org.springframework.aot.hint.RuntimeHints;
22+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
23+
import org.springframework.aot.hint.support.RuntimeHintsUtils;
24+
import org.springframework.lang.Nullable;
25+
import org.springframework.stereotype.Controller;
26+
27+
/**
28+
* {@link RuntimeHintsRegistrar} implementation that makes messaging
29+
* annotations available at runtime.
30+
*
31+
* @author Sebastien Deleuze
32+
* @since 6.0
33+
*/
34+
public class MessagingAnnotationsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
35+
36+
@Override
37+
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
38+
Stream.of(Controller.class, DestinationVariable.class, Header.class, Headers.class,
39+
MessageExceptionHandler.class, MessageMapping.class, Payload.class, SendTo.class).forEach(
40+
annotationType -> RuntimeHintsUtils.registerAnnotation(hints, annotationType));
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.messaging.simp.annotation;
18+
19+
import java.util.stream.Stream;
20+
21+
import org.springframework.aot.hint.RuntimeHints;
22+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
23+
import org.springframework.aot.hint.support.RuntimeHintsUtils;
24+
25+
/**
26+
* {@link RuntimeHintsRegistrar} implementation that makes Simp annotations
27+
* available at runtime.
28+
*
29+
* @author Sebastien Deleuze
30+
* @since 6.0
31+
*/
32+
public class SimpAnnotationsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
33+
34+
@Override
35+
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
36+
Stream.of(SendToUser.class, SubscribeMapping.class).forEach(
37+
annotationType -> RuntimeHintsUtils.registerAnnotation(hints, annotationType));
38+
}
39+
}

spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.context.ApplicationContext;
2929
import org.springframework.context.ApplicationContextAware;
3030
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.ImportRuntimeHints;
3132
import org.springframework.context.event.SmartApplicationListener;
3233
import org.springframework.core.task.TaskExecutor;
3334
import org.springframework.lang.Nullable;
@@ -41,10 +42,12 @@
4142
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
4243
import org.springframework.messaging.converter.MessageConverter;
4344
import org.springframework.messaging.converter.StringMessageConverter;
45+
import org.springframework.messaging.handler.annotation.MessagingAnnotationsRuntimeHintsRegistrar;
4446
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
4547
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
4648
import org.springframework.messaging.simp.SimpLogging;
4749
import org.springframework.messaging.simp.SimpMessagingTemplate;
50+
import org.springframework.messaging.simp.annotation.SimpAnnotationsRuntimeHintsRegistrar;
4851
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
4952
import org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler;
5053
import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
@@ -95,6 +98,7 @@
9598
* @author Sebastien Deleuze
9699
* @since 4.0
97100
*/
101+
@ImportRuntimeHints({ MessagingAnnotationsRuntimeHintsRegistrar.class, SimpAnnotationsRuntimeHintsRegistrar.class })
98102
public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware {
99103

100104
private static final String MVC_VALIDATOR_NAME = "mvcValidator";

0 commit comments

Comments
 (0)