Browse Source

Add configurable Predicate for type inclusion to TypeCollector.

Closes: #3362
Original Pull Request: #3363
pull/3368/head
Mark Paluch 3 months ago committed by Christoph Strobl
parent
commit
171f7b9de7
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 31
      src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java
  2. 15
      src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java
  3. 57
      src/main/java/org/springframework/data/util/TypeCollector.java
  4. 11
      src/test/java/org/springframework/data/aot/TypeCollectorUnitTests.java

31
src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java

@ -18,6 +18,7 @@ package org.springframework.data.aot;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -35,6 +36,7 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.data.domain.ManagedTypes; import org.springframework.data.domain.ManagedTypes;
import org.springframework.data.util.Lazy; import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeCollector;
import org.springframework.data.util.TypeContributor; import org.springframework.data.util.TypeContributor;
import org.springframework.data.util.TypeUtils; import org.springframework.data.util.TypeUtils;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -125,9 +127,18 @@ public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistratio
*/ */
protected BeanRegistrationAotContribution contribute(AotContext aotContext, ManagedTypes managedTypes, protected BeanRegistrationAotContribution contribute(AotContext aotContext, ManagedTypes managedTypes,
RegisteredBean registeredBean) { 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<TypeCollector> typeCollectorCustomizer() {
return typeCollector -> {};
}
/** /**
* Hook to contribute configuration for a given {@literal type}. * Hook to contribute configuration for a given {@literal type}.
* *
@ -142,14 +153,26 @@ public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistratio
Set<String> annotationNamespaces = Collections.singleton(TypeContributor.DATA_NAMESPACE); Set<String> annotationNamespaces = Collections.singleton(TypeContributor.DATA_NAMESPACE);
aotContext.typeConfiguration(type, config -> config.forDataBinding() // configureTypeContribution(type.toClass(), aotContext);
.contributeAccessors() //
.forQuerydsl().contribute(environment.get(), generationContext)); aotContext.typeConfiguration(type, config -> {
config.contribute(environment.get(), generationContext);
});
TypeUtils.resolveUsedAnnotations(type.toClass()).forEach( TypeUtils.resolveUsedAnnotations(type.toClass()).forEach(
annotation -> TypeContributor.contribute(annotation.getType(), annotationNamespaces, generationContext)); 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) { protected boolean isMatch(@Nullable Class<?> beanType, @Nullable String beanName) {
return matchesByType(beanType) && matchesPrefix(beanName); return matchesByType(beanType) && matchesPrefix(beanName);
} }

15
src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java

@ -18,11 +18,12 @@ package org.springframework.data.aot;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; import java.util.function.Consumer;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.aot.generate.AccessControl; import org.springframework.aot.generate.AccessControl;
import org.springframework.aot.generate.GeneratedMethod; import org.springframework.aot.generate.GeneratedMethod;
import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.GenerationContext;
@ -76,15 +77,18 @@ class ManagedTypesRegistrationAotContribution implements RegisteredBeanAotContri
private final AotContext aotContext; private final AotContext aotContext;
private final ManagedTypes managedTypes; private final ManagedTypes managedTypes;
private final Lazy<List<Class<?>>> sourceTypes; private final Lazy<List<Class<?>>> sourceTypes;
private final Consumer<TypeCollector> typeCollectorCustomizer;
private final TypeRegistration contributionAction; private final TypeRegistration contributionAction;
private final RegisteredBean source; private final RegisteredBean source;
public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes, public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes,
RegisteredBean registeredBean, TypeRegistration contributionAction) { RegisteredBean registeredBean, Consumer<TypeCollector> typeCollectorCustomizer,
TypeRegistration contributionAction) {
this.aotContext = aotContext; this.aotContext = aotContext;
this.managedTypes = managedTypes; this.managedTypes = managedTypes;
this.sourceTypes = Lazy.of(managedTypes::toList); this.sourceTypes = Lazy.of(managedTypes::toList);
this.typeCollectorCustomizer = typeCollectorCustomizer;
this.contributionAction = contributionAction; this.contributionAction = contributionAction;
this.source = registeredBean; this.source = registeredBean;
} }
@ -95,7 +99,8 @@ class ManagedTypesRegistrationAotContribution implements RegisteredBeanAotContri
List<Class<?>> types = sourceTypes.get(); List<Class<?>> types = sourceTypes.get();
if (!types.isEmpty()) { 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 @@ class ManagedTypesRegistrationAotContribution implements RegisteredBeanAotContri
public BeanRegistrationCodeFragments customizeBeanRegistrationCodeFragments(GenerationContext generationContext, public BeanRegistrationCodeFragments customizeBeanRegistrationCodeFragments(GenerationContext generationContext,
BeanRegistrationCodeFragments codeFragments) { BeanRegistrationCodeFragments codeFragments) {
if (managedTypes == null) {
return codeFragments;
}
ManagedTypesInstanceCodeFragment fragment = new ManagedTypesInstanceCodeFragment(sourceTypes.get(), source, ManagedTypesInstanceCodeFragment fragment = new ManagedTypesInstanceCodeFragment(sourceTypes.get(), source,
codeFragments); codeFragments);
return fragment.canGenerateCode() ? fragment : codeFragments; return fragment.canGenerateCode() ? fragment : codeFragments;

57
src/main/java/org/springframework/data/util/TypeCollector.java

@ -94,8 +94,40 @@ public class TypeCollector {
return inspect(Arrays.asList(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<Class<?>> types) { public static ReachableTypes inspect(Collection<Class<?>> 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<TypeCollector> 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<TypeCollector> collectorCustomizer, Collection<Class<?>> types) {
TypeCollector typeCollector = new TypeCollector();
collectorCustomizer.accept(typeCollector);
return new ReachableTypes(typeCollector, types);
} }
private void process(Class<?> root, Consumer<ResolvableType> consumer) { private void process(Class<?> root, Consumer<ResolvableType> consumer) {
@ -225,22 +257,37 @@ public class TypeCollector {
return (Predicate) excludedFieldPredicate.negate(); return (Predicate) excludedFieldPredicate.negate();
} }
/**
* Container for reachable types starting from a set of root types.
*/
public static class ReachableTypes { public static class ReachableTypes {
private final Iterable<Class<?>> roots; private final Iterable<Class<?>> roots;
private final Lazy<List<Class<?>>> reachableTypes = Lazy.of(this::collect); private final Lazy<List<Class<?>>> reachableTypes = Lazy.of(this::collect);
private final TypeCollector typeCollector; private final TypeCollector typeCollector;
public ReachableTypes(TypeCollector typeCollector, Iterable<Class<?>> roots) { ReachableTypes(TypeCollector typeCollector, Iterable<Class<?>> roots) {
this.typeCollector = typeCollector; this.typeCollector = typeCollector;
this.roots = roots; this.roots = roots;
} }
public void forEach(Consumer<ResolvableType> 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<ResolvableType> 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<Class<?>> list() { public List<Class<?>> list() {
return reachableTypes.get(); return reachableTypes.get();
} }
@ -248,7 +295,7 @@ public class TypeCollector {
private List<Class<?>> collect() { private List<Class<?>> collect() {
List<Class<?>> target = new ArrayList<>(); List<Class<?>> target = new ArrayList<>();
forEach(it -> target.add(it.toClass())); forEach(it -> target.add(it.toClass()));
return target; return List.copyOf(target);
} }
} }

11
src/test/java/org/springframework/data/aot/TypeCollectorUnitTests.java

@ -22,7 +22,10 @@ import org.springframework.data.aot.types.*;
import org.springframework.data.util.TypeCollector; import org.springframework.data.util.TypeCollector;
/** /**
* Unit tests for {@link TypeCollector}.
*
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch
*/ */
public class TypeCollectorUnitTests { public class TypeCollectorUnitTests {
@ -66,4 +69,12 @@ public class TypeCollectorUnitTests {
assertThat(TypeCollector.inspect(org.springframework.core.AliasRegistry.class).list()).isEmpty(); 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);
}
} }

Loading…
Cancel
Save