@ -36,6 +36,7 @@ import java.util.function.Predicate;
@@ -36,6 +36,7 @@ import java.util.function.Predicate;
import org.apache.commons.logging.Log ;
import org.apache.commons.logging.LogFactory ;
import org.springframework.beans.factory.aot.AotServices ;
import org.springframework.core.ResolvableType ;
import org.springframework.lang.Contract ;
import org.springframework.util.ObjectUtils ;
@ -46,44 +47,83 @@ import org.springframework.util.ReflectionUtils;
@@ -46,44 +47,83 @@ import org.springframework.util.ReflectionUtils;
* < p >
* Type inspection walks through all class members ( fields , methods , constructors ) and introspects those for additional
* types that are part of the domain model .
* < p >
* Type collection can be customized by providing filters that stop introspection when encountering a { @link Predicate }
* that returns { @code false } . Filters are { @link Predicate # and ( Predicate ) combined } so that multiple filters can be
* taken into account . A type / field / method must pass all filters to be considered for further inspection .
* < p >
* The collector uses { @link AotServices } to discover implementations of { @link TypeCollectorPredicateProvider } so that
* components using { @link TypeCollector } can contribute their own filtering logic to exclude types , fields , and methods
* from being inspected .
*
* @author Christoph Strobl
* @author Sebastien Deleuze
* @author John Blum
* @author Mark Paluch
* @since 3 . 0
* /
public class TypeCollector {
private static final Log logger = LogFactory . getLog ( TypeCollector . class ) ;
static final Set < String > EXCLUDED_DOMAINS = Set . of ( "java" , "sun." , "jdk." , "reactor." , "kotlinx." , "kotlin." , "org.springframework.core." ,
"org.springframework.data.mapping." , "org.springframework.data.repository." , "org.springframework.boot." ,
"org.springframework.context." , "org.springframework.beans." ) ;
private static final AotServices < TypeCollectorPredicateProvider > providers = AotServices . factories ( )
. load ( TypeCollectorPredicateProvider . class ) ;
private final Predicate < Class < ? > > excludedDomainsFilter = type - > {
String packageName = type . getPackageName ( ) + "." ;
return EXCLUDED_DOMAINS . stream ( ) . noneMatch ( packageName : : startsWith ) ;
} ;
private Predicate < Class < ? > > typeFilter = Predicates . isTrue ( ) ;
private Predicate < Class < ? > > typeFilter = excludedDomainsFilter
. and ( it - > ! it . isLocalClass ( ) & & ! it . isAnonymousClass ( ) ) ;
private Predicate < Method > methodFilter = Predicates . isTrue ( ) ;
private final Predicate < Method > methodFilter = createMethodFilter ( ) ;
private Predicate < Field > fieldFilter = Predicates . isTrue ( ) ;
private Predicate < Field > fieldFilter = createFieldFilter ( ) ;
/ * *
* Create a new { @link TypeCollector } applying all { @link TypeCollectorPredicateProvider } discovered through
* { @link AotServices } .
* /
public TypeCollector ( ) {
@Contract ( "_ -> this" )
public TypeCollector filterFields ( Predicate < Field > filter ) {
this . fieldFilter = filter . and ( filter ) ;
return this ;
providers . forEach ( provider - > {
filterTypes ( provider . classPredicate ( ) ) ;
filterMethods ( provider . methodPredicate ( ) ) ;
filterFields ( provider . fieldPredicate ( ) ) ;
} ) ;
}
/ * *
* Add a filter to exclude types from being introspected .
*
* @param filter filter predicate matching a { @link Class } .
* @return { @code this } TypeCollector instance .
* /
@Contract ( "_ -> this" )
public TypeCollector filterTypes ( Predicate < Class < ? > > filter ) {
this . typeFilter = this . typeFilter . and ( filter ) ;
return this ;
}
/ * *
* Add a filter to exclude methods from being introspected .
*
* @param filter filter predicate matching a { @link Class } .
* @return { @code this } TypeCollector instance .
* /
@Contract ( "_ -> this" )
public TypeCollector filterMethods ( Predicate < Method > filter ) {
this . methodFilter = methodFilter . and ( filter ) ;
return this ;
}
/ * *
* Add a filter to exclude fields from being introspected .
*
* @param filter filter predicate matching a { @link Class } .
* @return { @code this } TypeCollector instance .
* /
@Contract ( "_ -> this" )
public TypeCollector filterFields ( Predicate < Field > filter ) {
this . fieldFilter = fieldFilter . and ( filter ) ;
return this ;
}
/ * *
* Inspect the given type and resolve those reachable via fields , methods , generics , . . .
*
@ -162,7 +202,7 @@ public class TypeCollector {
@@ -162,7 +202,7 @@ public class TypeCollector {
}
}
Set < Type > visitConstructorsOfType ( ResolvableType type ) {
private Set < Type > visitConstructorsOfType ( ResolvableType type ) {
if ( ! typeFilter . test ( type . toClass ( ) ) ) {
return Collections . emptySet ( ) ;
@ -185,7 +225,7 @@ public class TypeCollector {
@@ -185,7 +225,7 @@ public class TypeCollector {
return new HashSet < > ( discoveredTypes ) ;
}
Set < Type > visitMethodsOfType ( ResolvableType type ) {
private Set < Type > visitMethodsOfType ( ResolvableType type ) {
if ( ! typeFilter . test ( type . toClass ( ) ) ) {
return Collections . emptySet ( ) ;
@ -210,7 +250,7 @@ public class TypeCollector {
@@ -210,7 +250,7 @@ public class TypeCollector {
return new HashSet < > ( discoveredTypes ) ;
}
Set < Type > visitFieldsOfType ( ResolvableType type ) {
private Set < Type > visitFieldsOfType ( ResolvableType type ) {
Set < Type > discoveredTypes = new LinkedHashSet < > ( ) ;
@ -228,35 +268,6 @@ public class TypeCollector {
@@ -228,35 +268,6 @@ public class TypeCollector {
return discoveredTypes ;
}
private Predicate < Method > createMethodFilter ( ) {
Predicate < Method > excludedDomainsPredicate = methodToTest - > excludedDomainsFilter
. test ( methodToTest . getDeclaringClass ( ) ) ;
Predicate < Method > excludedMethodsPredicate = Predicates . IS_BRIDGE_METHOD //
. or ( Predicates . IS_STATIC ) //
. or ( Predicates . IS_SYNTHETIC ) //
. or ( Predicates . IS_NATIVE ) //
. or ( Predicates . IS_PRIVATE ) //
. or ( Predicates . IS_PROTECTED ) //
. or ( Predicates . IS_OBJECT_MEMBER ) //
. or ( Predicates . IS_HIBERNATE_MEMBER ) //
. or ( Predicates . IS_ENUM_MEMBER ) //
. or ( excludedDomainsPredicate . negate ( ) ) ; //
return excludedMethodsPredicate . negate ( ) ;
}
@SuppressWarnings ( "rawtypes" )
private Predicate < Field > createFieldFilter ( ) {
Predicate < Member > excludedFieldPredicate = Predicates . IS_HIBERNATE_MEMBER //
. or ( Predicates . IS_SYNTHETIC ) //
. or ( Predicates . IS_JAVA ) ;
return ( Predicate ) excludedFieldPredicate . negate ( ) ;
}
/ * *
* Container for reachable types starting from a set of root types .
* /
@ -297,6 +308,7 @@ public class TypeCollector {
@@ -297,6 +308,7 @@ public class TypeCollector {
forEach ( it - > target . add ( it . toClass ( ) ) ) ;
return List . copyOf ( target ) ;
}
}
static class InspectionCache {
@ -322,5 +334,111 @@ public class TypeCollector {
@@ -322,5 +334,111 @@ public class TypeCollector {
public int size ( ) {
return mutableCache . size ( ) ;
}
}
/ * *
* Strategy interface providing predicates to filter types , fields , and methods from being introspected and
* contributed to AOT processing .
* < p >
* { @code BeanRegistrationAotProcessor } implementations must be registered in a
* { @value AotServices # FACTORIES_RESOURCE_LOCATION } resource . This interface serves as SPI and can be provided through
* { @link org . springframework . beans . factory . aot . AotServices } .
* < p >
* { @link TypeCollector } discovers all implementations and applies the combined predicates returned by this interface
* to filter unwanted reachable types from AOT contribution .
*
* @author Mark Paluch
* @since 4 . 0
* /
public interface TypeCollectorPredicateProvider {
/ * *
* Return a predicate to filter types .
*
* @return a predicate to filter types .
* /
default Predicate < Class < ? > > classPredicate ( ) {
return Predicates . isTrue ( ) ;
}
/ * *
* Return a predicate to filter fields .
*
* @return a predicate to filter fields .
* /
default Predicate < Field > fieldPredicate ( ) {
return Predicates . isTrue ( ) ;
}
/ * *
* Return a predicate to filter methods for method signature introspection . not provided .
*
* @return a predicate to filter methods .
* /
default Predicate < Method > methodPredicate ( ) {
return Predicates . isTrue ( ) ;
}
}
/ * *
* Default implementation of { @link TypeCollectorPredicateProvider } that excludes types from certain packages and
* filters out unwanted fields and methods .
*
* @since 4 . 0
* /
private static class DefaultTypeCollectorPredicateProvider implements TypeCollectorPredicateProvider {
private static final Set < String > EXCLUDED_DOMAINS = Set . of ( "java" , "sun." , "jdk." , "reactor." , "kotlinx." ,
"kotlin." , "org.springframework.core." , "org.springframework.data.mapping." ,
"org.springframework.data.repository." , "org.springframework.boot." , "org.springframework.context." ,
"org.springframework.beans." ) ;
private static final Predicate < Class < ? > > PACKAGE_PREDICATE = type - > {
String packageName = type . getPackageName ( ) + "." ;
for ( String excludedDomain : EXCLUDED_DOMAINS ) {
if ( packageName . startsWith ( excludedDomain ) ) {
return true ;
}
}
return false ;
} ;
private static final Predicate < Class < ? > > UNREACHABLE_CLASS = type - > type . isLocalClass ( ) | | type . isAnonymousClass ( ) ;
private static final Predicate < Member > UNWANTED_FIELDS = Predicates . IS_SYNTHETIC //
. or ( Predicates . IS_JAVA ) //
. or ( Predicates . declaringClass ( PACKAGE_PREDICATE ) ) ;
private static final Predicate < Method > UNWANTED_METHODS = Predicates . IS_BRIDGE_METHOD //
. or ( Predicates . IS_STATIC ) //
. or ( Predicates . IS_SYNTHETIC ) //
. or ( Predicates . IS_NATIVE ) //
. or ( Predicates . IS_PRIVATE ) //
. or ( Predicates . IS_PROTECTED ) //
. or ( Predicates . IS_OBJECT_MEMBER ) //
. or ( Predicates . IS_ENUM_MEMBER ) //
. or ( Predicates . declaringClass ( PACKAGE_PREDICATE ) ) ;
@Override
public Predicate < Class < ? > > classPredicate ( ) {
return UNREACHABLE_CLASS . or ( PACKAGE_PREDICATE ) . negate ( ) ;
}
@Override
public Predicate < Field > fieldPredicate ( ) {
return ( Predicate ) UNWANTED_FIELDS . negate ( ) ;
}
@Override
public Predicate < Method > methodPredicate ( ) {
return UNWANTED_METHODS . negate ( ) ;
}
}
}