36
36
import org .apache .commons .logging .Log ;
37
37
import org .apache .commons .logging .LogFactory ;
38
38
39
+ import org .springframework .beans .factory .aot .AotServices ;
39
40
import org .springframework .core .ResolvableType ;
40
41
import org .springframework .lang .Contract ;
41
42
import org .springframework .util .ObjectUtils ;
46
47
* <p>
47
48
* Type inspection walks through all class members (fields, methods, constructors) and introspects those for additional
48
49
* types that are part of the domain model.
50
+ * <p>
51
+ * Type collection can be customized by providing filters that stop introspection when encountering a {@link Predicate}
52
+ * that returns {@code false}. Filters are {@link Predicate#and(Predicate) combined} so that multiple filters can be
53
+ * taken into account. A type/field/method must pass all filters to be considered for further inspection.
54
+ * <p>
55
+ * The collector uses {@link AotServices} to discover implementations of {@link TypeCollectorPredicateProvider} so that
56
+ * components using {@link TypeCollector} can contribute their own filtering logic to exclude types, fields, and methods
57
+ * from being inspected.
49
58
*
50
59
* @author Christoph Strobl
51
60
* @author Sebastien Deleuze
52
61
* @author John Blum
62
+ * @author Mark Paluch
53
63
* @since 3.0
54
64
*/
55
65
public class TypeCollector {
56
66
57
67
private static final Log logger = LogFactory .getLog (TypeCollector .class );
58
68
59
- static final Set <String > EXCLUDED_DOMAINS = Set .of ("java" , "sun." , "jdk." , "reactor." , "kotlinx." , "kotlin." , "org.springframework.core." ,
60
- "org.springframework.data.mapping." , "org.springframework.data.repository." , "org.springframework.boot." ,
61
- "org.springframework.context." , "org.springframework.beans." );
69
+ private static final AotServices <TypeCollectorPredicateProvider > providers = AotServices .factories ()
70
+ .load (TypeCollectorPredicateProvider .class );
62
71
63
- private final Predicate <Class <?>> excludedDomainsFilter = type -> {
64
- String packageName = type .getPackageName () + "." ;
65
- return EXCLUDED_DOMAINS .stream ().noneMatch (packageName ::startsWith );
66
- };
72
+ private Predicate <Class <?>> typeFilter = Predicates .isTrue ();
67
73
68
- private Predicate <Class <?>> typeFilter = excludedDomainsFilter
69
- .and (it -> !it .isLocalClass () && !it .isAnonymousClass ());
74
+ private Predicate <Method > methodFilter = Predicates .isTrue ();
70
75
71
- private final Predicate <Method > methodFilter = createMethodFilter ();
76
+ private Predicate <Field > fieldFilter = Predicates . isTrue ();
72
77
73
- private Predicate <Field > fieldFilter = createFieldFilter ();
78
+ /**
79
+ * Create a new {@link TypeCollector} applying all {@link TypeCollectorPredicateProvider} discovered through
80
+ * {@link AotServices}.
81
+ */
82
+ public TypeCollector () {
74
83
75
- @ Contract ("_ -> this" )
76
- public TypeCollector filterFields (Predicate <Field > filter ) {
77
- this .fieldFilter = filter .and (filter );
78
- return this ;
84
+ providers .forEach (provider -> {
85
+ filterTypes (provider .classPredicate ());
86
+ filterMethods (provider .methodPredicate ());
87
+ filterFields (provider .fieldPredicate ());
88
+ });
79
89
}
80
90
91
+ /**
92
+ * Add a filter to exclude types from being introspected.
93
+ *
94
+ * @param filter filter predicate matching a {@link Class}.
95
+ * @return {@code this} TypeCollector instance.
96
+ */
81
97
@ Contract ("_ -> this" )
82
98
public TypeCollector filterTypes (Predicate <Class <?>> filter ) {
83
99
this .typeFilter = this .typeFilter .and (filter );
84
100
return this ;
85
101
}
86
102
103
+ /**
104
+ * Add a filter to exclude methods from being introspected.
105
+ *
106
+ * @param filter filter predicate matching a {@link Class}.
107
+ * @return {@code this} TypeCollector instance.
108
+ */
109
+ @ Contract ("_ -> this" )
110
+ public TypeCollector filterMethods (Predicate <Method > filter ) {
111
+ this .methodFilter = filter .and (filter );
112
+ return this ;
113
+ }
114
+
115
+ /**
116
+ * Add a filter to exclude fields from being introspected.
117
+ *
118
+ * @param filter filter predicate matching a {@link Class}.
119
+ * @return {@code this} TypeCollector instance.
120
+ */
121
+ @ Contract ("_ -> this" )
122
+ public TypeCollector filterFields (Predicate <Field > filter ) {
123
+ this .fieldFilter = filter .and (filter );
124
+ return this ;
125
+ }
126
+
87
127
/**
88
128
* Inspect the given type and resolve those reachable via fields, methods, generics, ...
89
129
*
@@ -162,7 +202,7 @@ private void processType(ResolvableType type, InspectionCache cache, Consumer<Re
162
202
}
163
203
}
164
204
165
- Set <Type > visitConstructorsOfType (ResolvableType type ) {
205
+ private Set <Type > visitConstructorsOfType (ResolvableType type ) {
166
206
167
207
if (!typeFilter .test (type .toClass ())) {
168
208
return Collections .emptySet ();
@@ -185,7 +225,7 @@ Set<Type> visitConstructorsOfType(ResolvableType type) {
185
225
return new HashSet <>(discoveredTypes );
186
226
}
187
227
188
- Set <Type > visitMethodsOfType (ResolvableType type ) {
228
+ private Set <Type > visitMethodsOfType (ResolvableType type ) {
189
229
190
230
if (!typeFilter .test (type .toClass ())) {
191
231
return Collections .emptySet ();
@@ -210,7 +250,7 @@ Set<Type> visitMethodsOfType(ResolvableType type) {
210
250
return new HashSet <>(discoveredTypes );
211
251
}
212
252
213
- Set <Type > visitFieldsOfType (ResolvableType type ) {
253
+ private Set <Type > visitFieldsOfType (ResolvableType type ) {
214
254
215
255
Set <Type > discoveredTypes = new LinkedHashSet <>();
216
256
@@ -228,35 +268,6 @@ Set<Type> visitFieldsOfType(ResolvableType type) {
228
268
return discoveredTypes ;
229
269
}
230
270
231
- private Predicate <Method > createMethodFilter () {
232
-
233
- Predicate <Method > excludedDomainsPredicate = methodToTest -> excludedDomainsFilter
234
- .test (methodToTest .getDeclaringClass ());
235
-
236
- Predicate <Method > excludedMethodsPredicate = Predicates .IS_BRIDGE_METHOD //
237
- .or (Predicates .IS_STATIC ) //
238
- .or (Predicates .IS_SYNTHETIC ) //
239
- .or (Predicates .IS_NATIVE ) //
240
- .or (Predicates .IS_PRIVATE ) //
241
- .or (Predicates .IS_PROTECTED ) //
242
- .or (Predicates .IS_OBJECT_MEMBER ) //
243
- .or (Predicates .IS_HIBERNATE_MEMBER ) //
244
- .or (Predicates .IS_ENUM_MEMBER ) //
245
- .or (excludedDomainsPredicate .negate ()); //
246
-
247
- return excludedMethodsPredicate .negate ();
248
- }
249
-
250
- @ SuppressWarnings ("rawtypes" )
251
- private Predicate <Field > createFieldFilter () {
252
-
253
- Predicate <Member > excludedFieldPredicate = Predicates .IS_HIBERNATE_MEMBER //
254
- .or (Predicates .IS_SYNTHETIC ) //
255
- .or (Predicates .IS_JAVA );
256
-
257
- return (Predicate ) excludedFieldPredicate .negate ();
258
- }
259
-
260
271
/**
261
272
* Container for reachable types starting from a set of root types.
262
273
*/
@@ -297,6 +308,7 @@ private List<Class<?>> collect() {
297
308
forEach (it -> target .add (it .toClass ()));
298
309
return List .copyOf (target );
299
310
}
311
+
300
312
}
301
313
302
314
static class InspectionCache {
@@ -322,5 +334,111 @@ public boolean isEmpty() {
322
334
public int size () {
323
335
return mutableCache .size ();
324
336
}
337
+
338
+ }
339
+
340
+ /**
341
+ * Strategy interface providing predicates to filter types, fields, and methods from being introspected and
342
+ * contributed to AOT processing.
343
+ * <p>
344
+ * {@code BeanRegistrationAotProcessor} implementations must be registered in a
345
+ * {@value AotServices#FACTORIES_RESOURCE_LOCATION} resource. This interface serves as SPI and can be provided through
346
+ * {@link org.springframework.beans.factory.aot.AotServices}.
347
+ * <p>
348
+ * {@link TypeCollector} discovers all implementations and applies the combined predicates returned by this interface
349
+ * to filter unwanted reachable types from AOT contribution.
350
+ *
351
+ * @author Mark Paluch
352
+ * @since 4.0
353
+ */
354
+ public interface TypeCollectorPredicateProvider {
355
+
356
+ /**
357
+ * Return a predicate to filter types.
358
+ *
359
+ * @return a predicate to filter types.
360
+ */
361
+ default Predicate <Class <?>> classPredicate () {
362
+ return Predicates .isTrue ();
363
+ }
364
+
365
+ /**
366
+ * Return a predicate to filter fields.
367
+ *
368
+ * @return a predicate to filter fields.
369
+ */
370
+ default Predicate <Field > fieldPredicate () {
371
+ return Predicates .isTrue ();
372
+ }
373
+
374
+ /**
375
+ * Return a predicate to filter methods for method signature introspection. not provided.
376
+ *
377
+ * @return a predicate to filter methods.
378
+ */
379
+ default Predicate <Method > methodPredicate () {
380
+ return Predicates .isTrue ();
381
+ }
382
+
383
+ }
384
+
385
+ /**
386
+ * Default implementation of {@link TypeCollectorPredicateProvider} that excludes types from certain packages and
387
+ * filters out unwanted fields and methods.
388
+ *
389
+ * @since 4.0
390
+ */
391
+ private static class DefaultTypeCollectorPredicateProvider implements TypeCollectorPredicateProvider {
392
+
393
+ private static final Set <String > EXCLUDED_DOMAINS = Set .of ("java" , "sun." , "jdk." , "reactor." , "kotlinx." ,
394
+ "kotlin." , "org.springframework.core." , "org.springframework.data.mapping." ,
395
+ "org.springframework.data.repository." , "org.springframework.boot." , "org.springframework.context." ,
396
+ "org.springframework.beans." );
397
+
398
+ private static final Predicate <Class <?>> PACKAGE_PREDICATE = type -> {
399
+
400
+ String packageName = type .getPackageName () + "." ;
401
+
402
+ for (String excludedDomain : EXCLUDED_DOMAINS ) {
403
+ if (packageName .startsWith (excludedDomain )) {
404
+ return true ;
405
+ }
406
+ }
407
+
408
+ return false ;
409
+ };
410
+
411
+ private static final Predicate <Class <?>> UNREACHABLE_CLASS = type -> type .isLocalClass () || type .isAnonymousClass ();
412
+
413
+ private static final Predicate <Member > UNWANTED_FIELDS = Predicates .IS_SYNTHETIC //
414
+ .or (Predicates .IS_JAVA ) //
415
+ .or (Predicates .declaringClass (PACKAGE_PREDICATE ));
416
+
417
+ private static final Predicate <Method > UNWANTED_METHODS = Predicates .IS_BRIDGE_METHOD //
418
+ .or (Predicates .IS_STATIC ) //
419
+ .or (Predicates .IS_SYNTHETIC ) //
420
+ .or (Predicates .IS_NATIVE ) //
421
+ .or (Predicates .IS_PRIVATE ) //
422
+ .or (Predicates .IS_PROTECTED ) //
423
+ .or (Predicates .IS_OBJECT_MEMBER ) //
424
+ .or (Predicates .IS_ENUM_MEMBER ) //
425
+ .or (Predicates .declaringClass (PACKAGE_PREDICATE ));
426
+
427
+ @ Override
428
+ public Predicate <Class <?>> classPredicate () {
429
+ return UNREACHABLE_CLASS .or (PACKAGE_PREDICATE ).negate ();
430
+ }
431
+
432
+ @ Override
433
+ public Predicate <Field > fieldPredicate () {
434
+ return (Predicate ) UNWANTED_FIELDS .negate ();
435
+ }
436
+
437
+ @ Override
438
+ public Predicate <Method > methodPredicate () {
439
+ return UNWANTED_METHODS .negate ();
440
+ }
441
+
325
442
}
443
+
326
444
}
0 commit comments