From e0a059a2a2b1798af43ddaf743940fc9f49cd232 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 16 Sep 2025 14:03:50 +0200 Subject: [PATCH 1/5] Add configurable Predicate for type inclusion to TypeCollector. --- ...agedTypesBeanRegistrationAotProcessor.java | 31 ++++++++-- ...nagedTypesRegistrationAotContribution.java | 15 ++--- .../data/util/TypeCollector.java | 57 +++++++++++++++++-- .../data/aot/TypeCollectorUnitTests.java | 11 ++++ 4 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java b/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java index e6935c5bb4..e084508801 100644 --- a/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java +++ b/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Set; +import java.util.function.Consumer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -35,6 +36,7 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.data.domain.ManagedTypes; import org.springframework.data.util.Lazy; +import org.springframework.data.util.TypeCollector; import org.springframework.data.util.TypeContributor; import org.springframework.data.util.TypeUtils; import org.springframework.util.ClassUtils; @@ -125,9 +127,18 @@ private ManagedTypes resolveManagedTypes(RegisteredBean registeredBean) { */ protected BeanRegistrationAotContribution contribute(AotContext aotContext, ManagedTypes managedTypes, RegisteredBean registeredBean) { - return new ManagedTypesRegistrationAotContribution(aotContext, managedTypes, registeredBean, this::contributeType); + return new ManagedTypesRegistrationAotContribution(aotContext, managedTypes, registeredBean, + typeCollectorCustomizer(), this::contributeType); } + /** + * Customization hook to configure {@link TypeCollector}. + * + * @return a {@link Consumer} to customize the {@link TypeCollector}, must not be {@literal null}. + */ + protected Consumer typeCollectorCustomizer() { + return typeCollector -> {}; + } /** * Hook to contribute configuration for a given {@literal type}. * @@ -142,14 +153,26 @@ protected void contributeType(ResolvableType type, GenerationContext generationC Set annotationNamespaces = Collections.singleton(TypeContributor.DATA_NAMESPACE); - aotContext.typeConfiguration(type, config -> config.forDataBinding() // - .contributeAccessors() // - .forQuerydsl().contribute(environment.get(), generationContext)); + configureTypeContribution(type.toClass(), aotContext); + + aotContext.typeConfiguration(type, config -> { + config.contribute(environment.get(), generationContext); + }); TypeUtils.resolveUsedAnnotations(type.toClass()).forEach( annotation -> TypeContributor.contribute(annotation.getType(), annotationNamespaces, generationContext)); } + /** + * Customization hook to configure the {@link TypeContributor} used to register the given {@literal type}. + * + * @param type the class to configure the contribution for. + * @param aotContext AOT context for type configuration. + */ + protected void configureTypeContribution(Class type, AotContext aotContext) { + aotContext.typeConfiguration(type, config -> config.forDataBinding().contributeAccessors().forQuerydsl()); + } + protected boolean isMatch(@Nullable Class beanType, @Nullable String beanName) { return matchesByType(beanType) && matchesPrefix(beanName); } diff --git a/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java b/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java index e86f8c161f..ea396ddbc9 100644 --- a/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java +++ b/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java @@ -18,11 +18,12 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.List; -import java.util.function.BiConsumer; +import java.util.function.Consumer; import javax.lang.model.element.Modifier; import org.jspecify.annotations.Nullable; + import org.springframework.aot.generate.AccessControl; import org.springframework.aot.generate.GeneratedMethod; import org.springframework.aot.generate.GenerationContext; @@ -76,15 +77,18 @@ class ManagedTypesRegistrationAotContribution implements RegisteredBeanAotContri private final AotContext aotContext; private final ManagedTypes managedTypes; private final Lazy>> sourceTypes; + private final Consumer typeCollectorCustomizer; private final TypeRegistration contributionAction; private final RegisteredBean source; public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes, - RegisteredBean registeredBean, TypeRegistration contributionAction) { + RegisteredBean registeredBean, Consumer typeCollectorCustomizer, + TypeRegistration contributionAction) { this.aotContext = aotContext; this.managedTypes = managedTypes; this.sourceTypes = Lazy.of(managedTypes::toList); + this.typeCollectorCustomizer = typeCollectorCustomizer; this.contributionAction = contributionAction; this.source = registeredBean; } @@ -95,7 +99,8 @@ public void applyTo(GenerationContext generationContext, BeanRegistrationCode be List> types = sourceTypes.get(); if (!types.isEmpty()) { - TypeCollector.inspect(types).forEach(type -> contributionAction.register(type, generationContext, aotContext)); + TypeCollector.inspect(typeCollectorCustomizer, types) + .forEach(type -> contributionAction.register(type, generationContext, aotContext)); } } @@ -103,10 +108,6 @@ public void applyTo(GenerationContext generationContext, BeanRegistrationCode be public BeanRegistrationCodeFragments customizeBeanRegistrationCodeFragments(GenerationContext generationContext, BeanRegistrationCodeFragments codeFragments) { - if (managedTypes == null) { - return codeFragments; - } - ManagedTypesInstanceCodeFragment fragment = new ManagedTypesInstanceCodeFragment(sourceTypes.get(), source, codeFragments); return fragment.canGenerateCode() ? fragment : codeFragments; diff --git a/src/main/java/org/springframework/data/util/TypeCollector.java b/src/main/java/org/springframework/data/util/TypeCollector.java index 0d605c8169..03cf4bbdeb 100644 --- a/src/main/java/org/springframework/data/util/TypeCollector.java +++ b/src/main/java/org/springframework/data/util/TypeCollector.java @@ -94,8 +94,40 @@ public static ReachableTypes inspect(Class... types) { return inspect(Arrays.asList(types)); } + /** + * Inspect the given type and resolve those reachable via fields, methods, generics, ... + * + * @param types the types to inspect + * @return a type model collector for the type + */ public static ReachableTypes inspect(Collection> types) { - return new ReachableTypes(new TypeCollector(), types); + return inspect(it -> {}, types); + } + + /** + * Inspect the given type and resolve those reachable via fields, methods, generics, ... + * + * @param collectorCustomizer the customizer function to configure the {@link TypeCollector}. + * @param types the types to inspect. + * @return a type model collector for the type. + * @since 4.0 + */ + public static ReachableTypes inspect(Consumer collectorCustomizer, Class... types) { + return inspect(collectorCustomizer, Arrays.asList(types)); + } + + /** + * Inspect the given type and resolve those reachable via fields, methods, generics, ... + * + * @param collectorCustomizer the customizer function to configure the {@link TypeCollector}. + * @param types the types to inspect. + * @return a type model collector for the type. + * @since 4.0 + */ + public static ReachableTypes inspect(Consumer collectorCustomizer, Collection> types) { + TypeCollector typeCollector = new TypeCollector(); + collectorCustomizer.accept(typeCollector); + return new ReachableTypes(typeCollector, types); } private void process(Class root, Consumer consumer) { @@ -225,22 +257,37 @@ private Predicate createFieldFilter() { return (Predicate) excludedFieldPredicate.negate(); } + /** + * Container for reachable types starting from a set of root types. + */ public static class ReachableTypes { private final Iterable> roots; private final Lazy>> reachableTypes = Lazy.of(this::collect); private final TypeCollector typeCollector; - public ReachableTypes(TypeCollector typeCollector, Iterable> roots) { + ReachableTypes(TypeCollector typeCollector, Iterable> roots) { this.typeCollector = typeCollector; this.roots = roots; } - public void forEach(Consumer consumer) { - roots.forEach(it -> typeCollector.process(it, consumer)); + /** + * Performs the given action for each element of the reachable types until all elements have been processed or the + * action throws an exception. Actions are performed in the order of iteration, if that order is specified. + * Exceptions thrown by the action are relayed to the caller. + * + * @param action The action to be performed for each element + */ + public void forEach(Consumer action) { + roots.forEach(it -> typeCollector.process(it, action)); } + /** + * Return all reachable types as list of {@link Class classes}. The resulting list is unmodifiable. + * + * @return an unmodifiable list of reachable types. + */ public List> list() { return reachableTypes.get(); } @@ -248,7 +295,7 @@ public List> list() { private List> collect() { List> target = new ArrayList<>(); forEach(it -> target.add(it.toClass())); - return target; + return List.copyOf(target); } } diff --git a/src/test/java/org/springframework/data/aot/TypeCollectorUnitTests.java b/src/test/java/org/springframework/data/aot/TypeCollectorUnitTests.java index 2c6574f4ff..83a7a53e41 100644 --- a/src/test/java/org/springframework/data/aot/TypeCollectorUnitTests.java +++ b/src/test/java/org/springframework/data/aot/TypeCollectorUnitTests.java @@ -22,7 +22,10 @@ import org.springframework.data.util.TypeCollector; /** + * Unit tests for {@link TypeCollector}. + * * @author Christoph Strobl + * @author Mark Paluch */ public class TypeCollectorUnitTests { @@ -66,4 +69,12 @@ void skipsCoreFrameworkType() { assertThat(TypeCollector.inspect(org.springframework.core.AliasRegistry.class).list()).isEmpty(); } + @Test // GH-3362 + void appliesFilterPredicate() { + assertThat(TypeCollector + .inspect(it -> it.filterTypes(cls -> cls == EmptyType1.class || cls == TypesInMethodSignatures.class), + TypesInMethodSignatures.class) + .list()).containsOnly(TypesInMethodSignatures.class, EmptyType1.class); + } + } From f9399c2593c6c93fec14ebaa4267b7fc2ba37237 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 16 Sep 2025 14:55:58 +0200 Subject: [PATCH 2/5] Introduce TypeCollectorPredicateProvider. We now provide a SPI for libraries that want to provide predicates for AOT processing so that AOT processing not only considers its own rules but also e.g. exclusions from other participants on the class path. --- ...nagedTypesRegistrationAotContribution.java | 2 - .../springframework/data/util/Predicates.java | 10 + .../data/util/TypeCollector.java | 212 ++++++++++++++---- .../resources/META-INF/spring/aot.factories | 2 + 4 files changed, 177 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java b/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java index ea396ddbc9..2463576a4d 100644 --- a/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java +++ b/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java @@ -75,7 +75,6 @@ class ManagedTypesRegistrationAotContribution implements RegisteredBeanAotContribution { private final AotContext aotContext; - private final ManagedTypes managedTypes; private final Lazy>> sourceTypes; private final Consumer typeCollectorCustomizer; private final TypeRegistration contributionAction; @@ -86,7 +85,6 @@ public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTyp TypeRegistration contributionAction) { this.aotContext = aotContext; - this.managedTypes = managedTypes; this.sourceTypes = Lazy.of(managedTypes::toList); this.typeCollectorCustomizer = typeCollectorCustomizer; this.contributionAction = contributionAction; diff --git a/src/main/java/org/springframework/data/util/Predicates.java b/src/main/java/org/springframework/data/util/Predicates.java index f8ca3ec5b8..82ef0742e2 100644 --- a/src/main/java/org/springframework/data/util/Predicates.java +++ b/src/main/java/org/springframework/data/util/Predicates.java @@ -51,6 +51,16 @@ public interface Predicates { Predicate IS_BRIDGE_METHOD = Method::isBridge; + /** + * A {@link Predicate} that introspects the declaring class of the member. + * + * @return a {@link Predicate} that introspects the declaring class of the member. + * @since 4.0 + */ + static Predicate declaringClass(Predicate> predicate) { + return t -> predicate.test(t.getDeclaringClass()); + } + /** * A {@link Predicate} that yields always {@code true}. * diff --git a/src/main/java/org/springframework/data/util/TypeCollector.java b/src/main/java/org/springframework/data/util/TypeCollector.java index 03cf4bbdeb..c61266a63b 100644 --- a/src/main/java/org/springframework/data/util/TypeCollector.java +++ b/src/main/java/org/springframework/data/util/TypeCollector.java @@ -36,6 +36,7 @@ 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 @@ *

* Type inspection walks through all class members (fields, methods, constructors) and introspects those for additional * types that are part of the domain model. + *

+ * 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. + *

+ * 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 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 providers = AotServices.factories() + .load(TypeCollectorPredicateProvider.class); - private final Predicate> excludedDomainsFilter = type -> { - String packageName = type.getPackageName() + "."; - return EXCLUDED_DOMAINS.stream().noneMatch(packageName::startsWith); - }; + private Predicate> typeFilter = Predicates.isTrue(); - private Predicate> typeFilter = excludedDomainsFilter - .and(it -> !it.isLocalClass() && !it.isAnonymousClass()); + private Predicate methodFilter = Predicates.isTrue(); - private final Predicate methodFilter = createMethodFilter(); + private Predicate fieldFilter = Predicates.isTrue(); - private Predicate fieldFilter = createFieldFilter(); + /** + * Create a new {@link TypeCollector} applying all {@link TypeCollectorPredicateProvider} discovered through + * {@link AotServices}. + */ + public TypeCollector() { - @Contract("_ -> this") - public TypeCollector filterFields(Predicate 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> 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 filter) { + this.methodFilter = filter.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 filter) { + this.fieldFilter = filter.and(filter); + return this; + } + /** * Inspect the given type and resolve those reachable via fields, methods, generics, ... * @@ -162,7 +202,7 @@ private void processType(ResolvableType type, InspectionCache cache, Consumer visitConstructorsOfType(ResolvableType type) { + private Set visitConstructorsOfType(ResolvableType type) { if (!typeFilter.test(type.toClass())) { return Collections.emptySet(); @@ -185,7 +225,7 @@ Set visitConstructorsOfType(ResolvableType type) { return new HashSet<>(discoveredTypes); } - Set visitMethodsOfType(ResolvableType type) { + private Set visitMethodsOfType(ResolvableType type) { if (!typeFilter.test(type.toClass())) { return Collections.emptySet(); @@ -210,7 +250,7 @@ Set visitMethodsOfType(ResolvableType type) { return new HashSet<>(discoveredTypes); } - Set visitFieldsOfType(ResolvableType type) { + private Set visitFieldsOfType(ResolvableType type) { Set discoveredTypes = new LinkedHashSet<>(); @@ -228,35 +268,6 @@ Set visitFieldsOfType(ResolvableType type) { return discoveredTypes; } - private Predicate createMethodFilter() { - - Predicate excludedDomainsPredicate = methodToTest -> excludedDomainsFilter - .test(methodToTest.getDeclaringClass()); - - Predicate 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 createFieldFilter() { - - Predicate 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 @@ private List> collect() { forEach(it -> target.add(it.toClass())); return List.copyOf(target); } + } static class InspectionCache { @@ -322,5 +334,111 @@ public boolean isEmpty() { public int size() { return mutableCache.size(); } + + } + + /** + * Strategy interface providing predicates to filter types, fields, and methods from being introspected and + * contributed to AOT processing. + *

+ * {@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}. + *

+ * {@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> classPredicate() { + return Predicates.isTrue(); + } + + /** + * Return a predicate to filter fields. + * + * @return a predicate to filter fields. + */ + default Predicate fieldPredicate() { + return Predicates.isTrue(); + } + + /** + * Return a predicate to filter methods for method signature introspection. not provided. + * + * @return a predicate to filter methods. + */ + default Predicate 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 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> PACKAGE_PREDICATE = type -> { + + String packageName = type.getPackageName() + "."; + + for (String excludedDomain : EXCLUDED_DOMAINS) { + if (packageName.startsWith(excludedDomain)) { + return true; + } + } + + return false; + }; + + private static final Predicate> UNREACHABLE_CLASS = type -> type.isLocalClass() || type.isAnonymousClass(); + + private static final Predicate UNWANTED_FIELDS = Predicates.IS_SYNTHETIC // + .or(Predicates.IS_JAVA) // + .or(Predicates.declaringClass(PACKAGE_PREDICATE)); + + private static final Predicate 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> classPredicate() { + return UNREACHABLE_CLASS.or(PACKAGE_PREDICATE).negate(); + } + + @Override + public Predicate fieldPredicate() { + return (Predicate) UNWANTED_FIELDS.negate(); + } + + @Override + public Predicate methodPredicate() { + return UNWANTED_METHODS.negate(); + } + } + } diff --git a/src/main/resources/META-INF/spring/aot.factories b/src/main/resources/META-INF/spring/aot.factories index 34bca58483..67e890a844 100644 --- a/src/main/resources/META-INF/spring/aot.factories +++ b/src/main/resources/META-INF/spring/aot.factories @@ -8,3 +8,5 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ org.springframework.data.aot.AuditingBeanRegistrationAotProcessor +org.springframework.data.util.TypeCollector$TypeCollectorPredicateProvider=\ + org.springframework.data.util.TypeCollector$DefaultTypeCollectorPredicateProvider From f8432a6661922c344d8a7a99026463edb09207e3 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 18 Sep 2025 09:31:07 +0200 Subject: [PATCH 3/5] Polishing. Rename TypeCollectorPredicateProvider -> TypeCollectorFilters Update javadoc and deprecate jpa specific predicate. --- ...ManagedTypesBeanRegistrationAotProcessor.java | 2 ++ .../springframework/data/util/Predicates.java | 8 +++----- .../springframework/data/util/TypeCollector.java | 16 +++++++++------- src/main/resources/META-INF/spring/aot.factories | 5 +++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java b/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java index e084508801..f3a99e17a7 100644 --- a/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java +++ b/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java @@ -135,6 +135,7 @@ protected BeanRegistrationAotContribution contribute(AotContext aotContext, Mana * Customization hook to configure {@link TypeCollector}. * * @return a {@link Consumer} to customize the {@link TypeCollector}, must not be {@literal null}. + * @since 4.0 */ protected Consumer typeCollectorCustomizer() { return typeCollector -> {}; @@ -168,6 +169,7 @@ protected void contributeType(ResolvableType type, GenerationContext generationC * * @param type the class to configure the contribution for. * @param aotContext AOT context for type configuration. + * @since 4.0 */ protected void configureTypeContribution(Class type, AotContext aotContext) { aotContext.typeConfiguration(type, config -> config.forDataBinding().contributeAccessors().forQuerydsl()); diff --git a/src/main/java/org/springframework/data/util/Predicates.java b/src/main/java/org/springframework/data/util/Predicates.java index 82ef0742e2..e22bce52f7 100644 --- a/src/main/java/org/springframework/data/util/Predicates.java +++ b/src/main/java/org/springframework/data/util/Predicates.java @@ -33,11 +33,9 @@ public interface Predicates { Predicate IS_ENUM_MEMBER = member -> member.getDeclaringClass().isEnum(); - Predicate IS_HIBERNATE_MEMBER = member -> member.getName().startsWith("$$_hibernate"); // this - // should - // go - // into - // JPA + + @Deprecated(since = "4.0", forRemoval = true) + Predicate IS_HIBERNATE_MEMBER = member -> member.getName().startsWith("$$_hibernate"); Predicate IS_OBJECT_MEMBER = member -> Object.class.equals(member.getDeclaringClass()); Predicate IS_JAVA = member -> member.getDeclaringClass().getPackageName().startsWith("java."); Predicate IS_NATIVE = member -> Modifier.isNative(member.getModifiers()); diff --git a/src/main/java/org/springframework/data/util/TypeCollector.java b/src/main/java/org/springframework/data/util/TypeCollector.java index c61266a63b..c7f12ee448 100644 --- a/src/main/java/org/springframework/data/util/TypeCollector.java +++ b/src/main/java/org/springframework/data/util/TypeCollector.java @@ -52,7 +52,7 @@ * 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. *

- * The collector uses {@link AotServices} to discover implementations of {@link TypeCollectorPredicateProvider} so that + * The collector uses {@link AotServices} to discover implementations of {@link TypeCollectorFilters} so that * components using {@link TypeCollector} can contribute their own filtering logic to exclude types, fields, and methods * from being inspected. * @@ -66,8 +66,8 @@ public class TypeCollector { private static final Log logger = LogFactory.getLog(TypeCollector.class); - private static final AotServices providers = AotServices.factories() - .load(TypeCollectorPredicateProvider.class); + private static final AotServices providers = AotServices.factories() + .load(TypeCollectorFilters.class); private Predicate> typeFilter = Predicates.isTrue(); @@ -76,7 +76,7 @@ public class TypeCollector { private Predicate fieldFilter = Predicates.isTrue(); /** - * Create a new {@link TypeCollector} applying all {@link TypeCollectorPredicateProvider} discovered through + * Create a new {@link TypeCollector} applying all {@link TypeCollectorFilters} discovered through * {@link AotServices}. */ public TypeCollector() { @@ -105,6 +105,7 @@ public TypeCollector filterTypes(Predicate> filter) { * * @param filter filter predicate matching a {@link Class}. * @return {@code this} TypeCollector instance. + * @since 4.0 */ @Contract("_ -> this") public TypeCollector filterMethods(Predicate filter) { @@ -117,6 +118,7 @@ public TypeCollector filterMethods(Predicate filter) { * * @param filter filter predicate matching a {@link Class}. * @return {@code this} TypeCollector instance. + * @since 4.0 */ @Contract("_ -> this") public TypeCollector filterFields(Predicate filter) { @@ -351,7 +353,7 @@ public int size() { * @author Mark Paluch * @since 4.0 */ - public interface TypeCollectorPredicateProvider { + public interface TypeCollectorFilters { /** * Return a predicate to filter types. @@ -383,12 +385,12 @@ default Predicate methodPredicate() { } /** - * Default implementation of {@link TypeCollectorPredicateProvider} that excludes types from certain packages and + * Default implementation of {@link TypeCollectorFilters} that excludes types from certain packages and * filters out unwanted fields and methods. * * @since 4.0 */ - private static class DefaultTypeCollectorPredicateProvider implements TypeCollectorPredicateProvider { + private static class DefaultTypeCollectorFilters implements TypeCollectorFilters { private static final Set EXCLUDED_DOMAINS = Set.of("java", "sun.", "jdk.", "reactor.", "kotlinx.", "kotlin.", "org.springframework.core.", "org.springframework.data.mapping.", diff --git a/src/main/resources/META-INF/spring/aot.factories b/src/main/resources/META-INF/spring/aot.factories index 67e890a844..977f848a81 100644 --- a/src/main/resources/META-INF/spring/aot.factories +++ b/src/main/resources/META-INF/spring/aot.factories @@ -8,5 +8,6 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ org.springframework.data.aot.AuditingBeanRegistrationAotProcessor -org.springframework.data.util.TypeCollector$TypeCollectorPredicateProvider=\ - org.springframework.data.util.TypeCollector$DefaultTypeCollectorPredicateProvider + +org.springframework.data.util.TypeCollector$TypeCollectorFilters=\ + org.springframework.data.util.TypeCollector$DefaultTypeCollectorFilters From 14caf37c0899b463cf824c2d4013313d9842cab9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 16 Sep 2025 13:59:02 +0200 Subject: [PATCH 4/5] Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 13143c9f6f..daaee056d9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-commons - 4.0.0-SNAPSHOT + 4.0.0-GH-3362-SNAPSHOT Spring Data Core Core Spring concepts underpinning every Spring Data module. From bd09434e7b45cf1fa51efa07fd5ef40bbe3f9459 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 18 Sep 2025 09:54:01 +0200 Subject: [PATCH 5/5] Fix field and method predicate concatenation. --- .../java/org/springframework/data/util/TypeCollector.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/util/TypeCollector.java b/src/main/java/org/springframework/data/util/TypeCollector.java index c7f12ee448..2e5f55b324 100644 --- a/src/main/java/org/springframework/data/util/TypeCollector.java +++ b/src/main/java/org/springframework/data/util/TypeCollector.java @@ -109,7 +109,7 @@ public TypeCollector filterTypes(Predicate> filter) { */ @Contract("_ -> this") public TypeCollector filterMethods(Predicate filter) { - this.methodFilter = filter.and(filter); + this.methodFilter = methodFilter.and(filter); return this; } @@ -122,7 +122,7 @@ public TypeCollector filterMethods(Predicate filter) { */ @Contract("_ -> this") public TypeCollector filterFields(Predicate filter) { - this.fieldFilter = filter.and(filter); + this.fieldFilter = fieldFilter.and(filter); return this; }