20
20
import java .lang .reflect .Method ;
21
21
import java .lang .reflect .Parameter ;
22
22
import java .lang .reflect .Type ;
23
+ import java .util .ArrayList ;
23
24
import java .util .Arrays ;
25
+ import java .util .List ;
24
26
25
27
import org .springframework .aop .SpringProxy ;
26
28
import org .springframework .aot .generate .GenerationContext ;
45
47
import org .springframework .graphql .data .method .HandlerMethodArgumentResolver ;
46
48
import org .springframework .graphql .data .method .HandlerMethodArgumentResolverComposite ;
47
49
import org .springframework .graphql .data .method .annotation .BatchMapping ;
50
+ import org .springframework .graphql .data .method .annotation .GraphQlExceptionHandler ;
48
51
import org .springframework .graphql .data .method .annotation .SchemaMapping ;
49
52
import org .springframework .stereotype .Controller ;
50
53
import org .springframework .util .ClassUtils ;
51
54
import org .springframework .util .ReflectionUtils ;
55
+ import org .springframework .web .bind .annotation .ControllerAdvice ;
52
56
53
57
import static org .springframework .core .annotation .MergedAnnotations .SearchStrategy .TYPE_HIERARCHY ;
54
58
58
62
* <ul>
59
63
* <li>invocation reflection on {@code @SchemaMapping} and {@code @BatchMapping}
60
64
* annotated controllers methods
65
+ * <li>invocation reflection on {@code @GraphQlExceptionHandler} methods
66
+ * in {@code @Controller} and {@code @ControllerAdvice} beans
61
67
* <li>binding reflection on controller method arguments, needed for binding or
62
68
* by the GraphQL Java engine itself
63
69
* <li>reflection for SpEL support and JDK proxy creation for
@@ -85,27 +91,42 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI
85
91
86
92
@ Override
87
93
public BeanFactoryInitializationAotContribution processAheadOfTime (ConfigurableListableBeanFactory beanFactory ) {
88
- Class <?>[] controllerTypes = Arrays .stream (beanFactory .getBeanDefinitionNames ())
94
+ List <Class <?>> controllers = new ArrayList <>();
95
+ List <Class <?>> controllerAdvices = new ArrayList <>();
96
+ Arrays .stream (beanFactory .getBeanDefinitionNames ())
89
97
.map (beanName -> RegisteredBean .of (beanFactory , beanName ).getBeanClass ())
90
- .filter (this ::isController )
91
- .toArray (Class <?>[]::new );
92
- return new SchemaMappingBeanFactoryInitializationAotContribution (controllerTypes );
98
+ .forEach (beanClass -> {
99
+ if (isController (beanClass )) {
100
+ controllers .add (beanClass );
101
+ }
102
+ else if (isControllerAdvice (beanClass )) {
103
+ controllerAdvices .add (beanClass );
104
+ }
105
+ });
106
+ return new SchemaMappingBeanFactoryInitializationAotContribution (controllers , controllerAdvices );
93
107
}
94
108
95
109
private boolean isController (AnnotatedElement element ) {
96
110
return MergedAnnotations .from (element , TYPE_HIERARCHY ).isPresent (Controller .class );
97
111
}
98
112
113
+ private boolean isControllerAdvice (AnnotatedElement element ) {
114
+ return MergedAnnotations .from (element , TYPE_HIERARCHY ).isPresent (ControllerAdvice .class );
115
+ }
116
+
99
117
100
118
private static class SchemaMappingBeanFactoryInitializationAotContribution
101
119
implements BeanFactoryInitializationAotContribution {
102
120
103
- private final Class <?>[] controllers ;
121
+ private final List <Class <?>> controllers ;
122
+
123
+ private final List <Class <?>> controllerAdvices ;
104
124
105
125
private final HandlerMethodArgumentResolverComposite argumentResolvers ;
106
126
107
- public SchemaMappingBeanFactoryInitializationAotContribution (Class <?>[] controllers ) {
127
+ public SchemaMappingBeanFactoryInitializationAotContribution (List < Class <?>> controllers , List < Class <?>> controllerAdvices ) {
108
128
this .controllers = controllers ;
129
+ this .controllerAdvices = controllerAdvices ;
109
130
this .argumentResolvers = createArgumentResolvers ();
110
131
}
111
132
@@ -120,11 +141,20 @@ private HandlerMethodArgumentResolverComposite createArgumentResolvers() {
120
141
public void applyTo (GenerationContext context , BeanFactoryInitializationCode initializationCode ) {
121
142
RuntimeHints runtimeHints = context .getRuntimeHints ();
122
143
registerSpringDataSpelSupport (runtimeHints );
123
- Arrays . stream ( this .controllers ) .forEach (controller -> {
124
- runtimeHints .reflection ().registerType (controller );
144
+ this .controllers .forEach (controller -> {
145
+ runtimeHints .reflection ().registerType (controller , MemberCategory . INTROSPECT_DECLARED_METHODS );
125
146
ReflectionUtils .doWithMethods (controller ,
126
147
method -> processSchemaMappingMethod (runtimeHints , method ),
127
148
this ::isGraphQlHandlerMethod );
149
+ ReflectionUtils .doWithMethods (controller ,
150
+ method -> processExceptionHandlerMethod (runtimeHints , method ),
151
+ this ::isExceptionHandlerMethod );
152
+ });
153
+ this .controllerAdvices .forEach (controllerAdvice -> {
154
+ runtimeHints .reflection ().registerType (controllerAdvice , MemberCategory .INTROSPECT_DECLARED_METHODS );
155
+ ReflectionUtils .doWithMethods (controllerAdvice ,
156
+ method -> processExceptionHandlerMethod (runtimeHints , method ),
157
+ this ::isExceptionHandlerMethod );
128
158
});
129
159
}
130
160
@@ -143,6 +173,10 @@ private boolean isGraphQlHandlerMethod(AnnotatedElement element) {
143
173
return annotations .isPresent (SchemaMapping .class ) || annotations .isPresent (BatchMapping .class );
144
174
}
145
175
176
+ private boolean isExceptionHandlerMethod (AnnotatedElement element ) {
177
+ return MergedAnnotations .from (element , TYPE_HIERARCHY ).isPresent (GraphQlExceptionHandler .class );
178
+ }
179
+
146
180
private void processSchemaMappingMethod (RuntimeHints runtimeHints , Method method ) {
147
181
runtimeHints .reflection ().registerMethod (method , ExecutableMode .INVOKE );
148
182
for (Parameter parameter : method .getParameters ()) {
@@ -151,6 +185,10 @@ private void processSchemaMappingMethod(RuntimeHints runtimeHints, Method method
151
185
processReturnType (runtimeHints , MethodParameter .forExecutable (method , -1 ));
152
186
}
153
187
188
+ private void processExceptionHandlerMethod (RuntimeHints runtimeHints , Method method ) {
189
+ runtimeHints .reflection ().registerMethod (method , ExecutableMode .INVOKE );
190
+ }
191
+
154
192
private void processMethodParameter (RuntimeHints hints , MethodParameter parameter ) {
155
193
MethodParameterRuntimeHintsRegistrar .fromMethodParameter (this .argumentResolvers , parameter ).apply (hints );
156
194
}
0 commit comments