@@ -99,8 +99,9 @@ private static <C, R> R processClass(C context, Class<?> source,
99
99
return switch (searchStrategy ) {
100
100
case DIRECT -> processElement (context , source , processor );
101
101
case INHERITED_ANNOTATIONS -> processClassInheritedAnnotations (context , source , searchStrategy , processor );
102
- case SUPERCLASS -> processClassHierarchy (context , source , processor , false );
103
- case TYPE_HIERARCHY -> processClassHierarchy (context , source , processor , true );
102
+ case SUPERCLASS -> processClassHierarchy (context , source , processor , false , false );
103
+ case TYPE_HIERARCHY -> processClassHierarchy (context , source , processor , true , false );
104
+ case TYPE_HIERARCHY_AND_ENCLOSING_CLASSES -> processClassHierarchy (context , source , processor , true , true );
104
105
};
105
106
}
106
107
@@ -160,14 +161,15 @@ private static <C, R> R processClassInheritedAnnotations(C context, Class<?> sou
160
161
161
162
@ Nullable
162
163
private static <C , R > R processClassHierarchy (C context , Class <?> source ,
163
- AnnotationsProcessor <C , R > processor , boolean includeInterfaces ) {
164
+ AnnotationsProcessor <C , R > processor , boolean includeInterfaces , boolean includeEnclosing ) {
164
165
165
- return processClassHierarchy (context , new int [] {0 }, source , processor , includeInterfaces );
166
+ return processClassHierarchy (context , new int [] {0 }, source , processor ,
167
+ includeInterfaces , includeEnclosing );
166
168
}
167
169
168
170
@ Nullable
169
171
private static <C , R > R processClassHierarchy (C context , int [] aggregateIndex , Class <?> source ,
170
- AnnotationsProcessor <C , R > processor , boolean includeInterfaces ) {
172
+ AnnotationsProcessor <C , R > processor , boolean includeInterfaces , boolean includeEnclosing ) {
171
173
172
174
try {
173
175
R result = processor .doWithAggregate (context , aggregateIndex [0 ]);
@@ -186,7 +188,7 @@ private static <C, R> R processClassHierarchy(C context, int[] aggregateIndex, C
186
188
if (includeInterfaces ) {
187
189
for (Class <?> interfaceType : source .getInterfaces ()) {
188
190
R interfacesResult = processClassHierarchy (context , aggregateIndex ,
189
- interfaceType , processor , true );
191
+ interfaceType , processor , true , includeEnclosing );
190
192
if (interfacesResult != null ) {
191
193
return interfacesResult ;
192
194
}
@@ -195,11 +197,31 @@ private static <C, R> R processClassHierarchy(C context, int[] aggregateIndex, C
195
197
Class <?> superclass = source .getSuperclass ();
196
198
if (superclass != Object .class && superclass != null ) {
197
199
R superclassResult = processClassHierarchy (context , aggregateIndex ,
198
- superclass , processor , includeInterfaces );
200
+ superclass , processor , includeInterfaces , includeEnclosing );
199
201
if (superclassResult != null ) {
200
202
return superclassResult ;
201
203
}
202
204
}
205
+ if (includeEnclosing ) {
206
+ // Since merely attempting to load the enclosing class may result in
207
+ // automatic loading of sibling nested classes that in turn results
208
+ // in an exception such as NoClassDefFoundError, we wrap the following
209
+ // in its own dedicated try-catch block in order not to preemptively
210
+ // halt the annotation scanning process.
211
+ try {
212
+ Class <?> enclosingClass = source .getEnclosingClass ();
213
+ if (enclosingClass != null ) {
214
+ R enclosingResult = processClassHierarchy (context , aggregateIndex ,
215
+ enclosingClass , processor , includeInterfaces , true );
216
+ if (enclosingResult != null ) {
217
+ return enclosingResult ;
218
+ }
219
+ }
220
+ }
221
+ catch (Throwable ex ) {
222
+ AnnotationUtils .handleIntrospectionFailure (source , ex );
223
+ }
224
+ }
203
225
}
204
226
catch (Throwable ex ) {
205
227
AnnotationUtils .handleIntrospectionFailure (source , ex );
@@ -216,7 +238,8 @@ private static <C, R> R processMethod(C context, Method source,
216
238
case DIRECT , INHERITED_ANNOTATIONS -> processMethodInheritedAnnotations (context , source , processor );
217
239
case SUPERCLASS -> processMethodHierarchy (context , new int []{0 }, source .getDeclaringClass (),
218
240
processor , source , false );
219
- case TYPE_HIERARCHY -> processMethodHierarchy (context , new int []{0 }, source .getDeclaringClass (),
241
+ case TYPE_HIERARCHY , TYPE_HIERARCHY_AND_ENCLOSING_CLASSES -> processMethodHierarchy (context , new int []{0 },
242
+ source .getDeclaringClass (),
220
243
processor , source , true );
221
244
};
222
245
}
@@ -483,8 +506,12 @@ private static boolean isWithoutHierarchy(AnnotatedElement source, SearchStrateg
483
506
if (source == Object .class ) {
484
507
return true ;
485
508
}
486
- if (source instanceof Class <?> sourceClass ) {
487
- return (sourceClass .getSuperclass () == Object .class && sourceClass .getInterfaces ().length == 0 );
509
+ if (source instanceof Class ) {
510
+ Class <?> sourceClass = (Class <?>) source ;
511
+ boolean noSuperTypes = (sourceClass .getSuperclass () == Object .class &&
512
+ sourceClass .getInterfaces ().length == 0 );
513
+ return (searchStrategy == SearchStrategy .TYPE_HIERARCHY_AND_ENCLOSING_CLASSES ? noSuperTypes &&
514
+ sourceClass .getEnclosingClass () == null : noSuperTypes );
488
515
}
489
516
if (source instanceof Method sourceMethod ) {
490
517
return (Modifier .isPrivate (sourceMethod .getModifiers ()) ||
0 commit comments