3636import  org .apache .commons .logging .Log ;
3737import  org .apache .commons .logging .LogFactory ;
3838
39+ import  org .springframework .beans .factory .aot .AotServices ;
3940import  org .springframework .core .ResolvableType ;
4041import  org .springframework .lang .Contract ;
4142import  org .springframework .util .ObjectUtils ;
4647 * <p> 
4748 * Type inspection walks through all class members (fields, methods, constructors) and introspects those for additional 
4849 * 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. 
4958 * 
5059 * @author Christoph Strobl 
5160 * @author Sebastien Deleuze 
5261 * @author John Blum 
62+  * @author Mark Paluch 
5363 * @since 3.0 
5464 */ 
5565public  class  TypeCollector  {
5666
5767	private  static  final  Log  logger  = LogFactory .getLog (TypeCollector .class );
5868
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 );
6271
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 ();
6773
68- 	private  Predicate <Class <?>> typeFilter  = excludedDomainsFilter 
69- 			.and (it  -> !it .isLocalClass () && !it .isAnonymousClass ());
74+ 	private  Predicate <Method > methodFilter  = Predicates .isTrue ();
7075
71- 	private  final   Predicate <Method >  methodFilter  = createMethodFilter ();
76+ 	private  Predicate <Field >  fieldFilter  = Predicates . isTrue ();
7277
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 () {
7483
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+ 		});
7989	}
8090
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+ 	 */ 
8197	@ Contract ("_ -> this" )
8298	public  TypeCollector  filterTypes (Predicate <Class <?>> filter ) {
8399		this .typeFilter  = this .typeFilter .and (filter );
84100		return  this ;
85101	}
86102
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+ 
87127	/** 
88128	 * Inspect the given type and resolve those reachable via fields, methods, generics, ... 
89129	 * 
@@ -162,7 +202,7 @@ private void processType(ResolvableType type, InspectionCache cache, Consumer<Re
162202		}
163203	}
164204
165- 	Set <Type > visitConstructorsOfType (ResolvableType  type ) {
205+ 	private   Set <Type > visitConstructorsOfType (ResolvableType  type ) {
166206
167207		if  (!typeFilter .test (type .toClass ())) {
168208			return  Collections .emptySet ();
@@ -185,7 +225,7 @@ Set<Type> visitConstructorsOfType(ResolvableType type) {
185225		return  new  HashSet <>(discoveredTypes );
186226	}
187227
188- 	Set <Type > visitMethodsOfType (ResolvableType  type ) {
228+ 	private   Set <Type > visitMethodsOfType (ResolvableType  type ) {
189229
190230		if  (!typeFilter .test (type .toClass ())) {
191231			return  Collections .emptySet ();
@@ -210,7 +250,7 @@ Set<Type> visitMethodsOfType(ResolvableType type) {
210250		return  new  HashSet <>(discoveredTypes );
211251	}
212252
213- 	Set <Type > visitFieldsOfType (ResolvableType  type ) {
253+ 	private   Set <Type > visitFieldsOfType (ResolvableType  type ) {
214254
215255		Set <Type > discoveredTypes  = new  LinkedHashSet <>();
216256
@@ -228,35 +268,6 @@ Set<Type> visitFieldsOfType(ResolvableType type) {
228268		return  discoveredTypes ;
229269	}
230270
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- 
260271	/** 
261272	 * Container for reachable types starting from a set of root types. 
262273	 */ 
@@ -297,6 +308,7 @@ private List<Class<?>> collect() {
297308			forEach (it  -> target .add (it .toClass ()));
298309			return  List .copyOf (target );
299310		}
311+ 
300312	}
301313
302314	static  class  InspectionCache  {
@@ -322,5 +334,111 @@ public boolean isEmpty() {
322334		public  int  size () {
323335			return  mutableCache .size ();
324336		}
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+ 
325442	}
443+ 
326444}
0 commit comments