Skip to content

Commit 880a14e

Browse files
committed
Add configurable Predicate for type inclusion to TypeCollector.
1 parent 017bbbf commit 880a14e

File tree

4 files changed

+98
-16
lines changed

4 files changed

+98
-16
lines changed

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Collection;
1919
import java.util.Collections;
2020
import java.util.Set;
21+
import java.util.function.Consumer;
2122

2223
import org.apache.commons.logging.Log;
2324
import org.apache.commons.logging.LogFactory;
@@ -35,6 +36,7 @@
3536
import org.springframework.core.env.StandardEnvironment;
3637
import org.springframework.data.domain.ManagedTypes;
3738
import org.springframework.data.util.Lazy;
39+
import org.springframework.data.util.TypeCollector;
3840
import org.springframework.data.util.TypeContributor;
3941
import org.springframework.data.util.TypeUtils;
4042
import org.springframework.util.ClassUtils;
@@ -125,9 +127,18 @@ private ManagedTypes resolveManagedTypes(RegisteredBean registeredBean) {
125127
*/
126128
protected BeanRegistrationAotContribution contribute(AotContext aotContext, ManagedTypes managedTypes,
127129
RegisteredBean registeredBean) {
128-
return new ManagedTypesRegistrationAotContribution(aotContext, managedTypes, registeredBean, this::contributeType);
130+
return new ManagedTypesRegistrationAotContribution(aotContext, managedTypes, registeredBean,
131+
typeCollectorCustomizer(), this::contributeType);
129132
}
130133

134+
/**
135+
* Customization hook to configure {@link TypeCollector}.
136+
*
137+
* @return a {@link Consumer} to customize the {@link TypeCollector}, must not be {@literal null}.
138+
*/
139+
protected Consumer<TypeCollector> typeCollectorCustomizer() {
140+
return typeCollector -> {};
141+
}
131142
/**
132143
* Hook to contribute configuration for a given {@literal type}.
133144
*
@@ -142,14 +153,26 @@ protected void contributeType(ResolvableType type, GenerationContext generationC
142153

143154
Set<String> annotationNamespaces = Collections.singleton(TypeContributor.DATA_NAMESPACE);
144155

145-
aotContext.typeConfiguration(type, config -> config.forDataBinding() //
146-
.contributeAccessors() //
147-
.forQuerydsl().contribute(environment.get(), generationContext));
156+
configureTypeContribution(type.toClass(), aotContext);
157+
158+
aotContext.typeConfiguration(type, config -> {
159+
config.contribute(environment.get(), generationContext);
160+
});
148161

149162
TypeUtils.resolveUsedAnnotations(type.toClass()).forEach(
150163
annotation -> TypeContributor.contribute(annotation.getType(), annotationNamespaces, generationContext));
151164
}
152165

166+
/**
167+
* Customization hook to configure the {@link TypeContributor} used to register the given {@literal type}.
168+
*
169+
* @param type the class to configure the contribution for.
170+
* @param aotContext AOT context for type configuration.
171+
*/
172+
protected void configureTypeContribution(Class<?> type, AotContext aotContext) {
173+
aotContext.typeConfiguration(type, config -> config.forDataBinding().contributeAccessors().forQuerydsl());
174+
}
175+
153176
protected boolean isMatch(@Nullable Class<?> beanType, @Nullable String beanName) {
154177
return matchesByType(beanType) && matchesPrefix(beanName);
155178
}

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
import java.lang.reflect.Method;
1919
import java.util.Collections;
2020
import java.util.List;
21-
import java.util.function.BiConsumer;
21+
import java.util.function.Consumer;
2222

2323
import javax.lang.model.element.Modifier;
2424

2525
import org.jspecify.annotations.Nullable;
26+
2627
import org.springframework.aot.generate.AccessControl;
2728
import org.springframework.aot.generate.GeneratedMethod;
2829
import org.springframework.aot.generate.GenerationContext;
@@ -76,15 +77,18 @@ class ManagedTypesRegistrationAotContribution implements RegisteredBeanAotContri
7677
private final AotContext aotContext;
7778
private final ManagedTypes managedTypes;
7879
private final Lazy<List<Class<?>>> sourceTypes;
80+
private final Consumer<TypeCollector> typeCollectorCustomizer;
7981
private final TypeRegistration contributionAction;
8082
private final RegisteredBean source;
8183

8284
public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes,
83-
RegisteredBean registeredBean, TypeRegistration contributionAction) {
85+
RegisteredBean registeredBean, Consumer<TypeCollector> typeCollectorCustomizer,
86+
TypeRegistration contributionAction) {
8487

8588
this.aotContext = aotContext;
8689
this.managedTypes = managedTypes;
8790
this.sourceTypes = Lazy.of(managedTypes::toList);
91+
this.typeCollectorCustomizer = typeCollectorCustomizer;
8892
this.contributionAction = contributionAction;
8993
this.source = registeredBean;
9094
}
@@ -95,18 +99,15 @@ public void applyTo(GenerationContext generationContext, BeanRegistrationCode be
9599
List<Class<?>> types = sourceTypes.get();
96100

97101
if (!types.isEmpty()) {
98-
TypeCollector.inspect(types).forEach(type -> contributionAction.register(type, generationContext, aotContext));
102+
TypeCollector.inspect(typeCollectorCustomizer, types)
103+
.forEach(type -> contributionAction.register(type, generationContext, aotContext));
99104
}
100105
}
101106

102107
@Override
103108
public BeanRegistrationCodeFragments customizeBeanRegistrationCodeFragments(GenerationContext generationContext,
104109
BeanRegistrationCodeFragments codeFragments) {
105110

106-
if (managedTypes == null) {
107-
return codeFragments;
108-
}
109-
110111
ManagedTypesInstanceCodeFragment fragment = new ManagedTypesInstanceCodeFragment(sourceTypes.get(), source,
111112
codeFragments);
112113
return fragment.canGenerateCode() ? fragment : codeFragments;

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

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,40 @@ public static ReachableTypes inspect(Class<?>... types) {
9494
return inspect(Arrays.asList(types));
9595
}
9696

97+
/**
98+
* Inspect the given type and resolve those reachable via fields, methods, generics, ...
99+
*
100+
* @param types the types to inspect
101+
* @return a type model collector for the type
102+
*/
97103
public static ReachableTypes inspect(Collection<Class<?>> types) {
98-
return new ReachableTypes(new TypeCollector(), types);
104+
return inspect(it -> {}, types);
105+
}
106+
107+
/**
108+
* Inspect the given type and resolve those reachable via fields, methods, generics, ...
109+
*
110+
* @param collectorCustomizer the customizer function to configure the {@link TypeCollector}.
111+
* @param types the types to inspect.
112+
* @return a type model collector for the type.
113+
* @since 4.0
114+
*/
115+
public static ReachableTypes inspect(Consumer<TypeCollector> collectorCustomizer, Class<?>... types) {
116+
return inspect(collectorCustomizer, Arrays.asList(types));
117+
}
118+
119+
/**
120+
* Inspect the given type and resolve those reachable via fields, methods, generics, ...
121+
*
122+
* @param collectorCustomizer the customizer function to configure the {@link TypeCollector}.
123+
* @param types the types to inspect.
124+
* @return a type model collector for the type.
125+
* @since 4.0
126+
*/
127+
public static ReachableTypes inspect(Consumer<TypeCollector> collectorCustomizer, Collection<Class<?>> types) {
128+
TypeCollector typeCollector = new TypeCollector();
129+
collectorCustomizer.accept(typeCollector);
130+
return new ReachableTypes(typeCollector, types);
99131
}
100132

101133
private void process(Class<?> root, Consumer<ResolvableType> consumer) {
@@ -225,30 +257,45 @@ private Predicate<Field> createFieldFilter() {
225257
return (Predicate) excludedFieldPredicate.negate();
226258
}
227259

260+
/**
261+
* Container for reachable types starting from a set of root types.
262+
*/
228263
public static class ReachableTypes {
229264

230265
private final Iterable<Class<?>> roots;
231266
private final Lazy<List<Class<?>>> reachableTypes = Lazy.of(this::collect);
232267
private final TypeCollector typeCollector;
233268

234-
public ReachableTypes(TypeCollector typeCollector, Iterable<Class<?>> roots) {
269+
ReachableTypes(TypeCollector typeCollector, Iterable<Class<?>> roots) {
235270

236271
this.typeCollector = typeCollector;
237272
this.roots = roots;
238273
}
239274

240-
public void forEach(Consumer<ResolvableType> consumer) {
241-
roots.forEach(it -> typeCollector.process(it, consumer));
275+
/**
276+
* Performs the given action for each element of the reachable types until all elements have been processed or the
277+
* action throws an exception. Actions are performed in the order of iteration, if that order is specified.
278+
* Exceptions thrown by the action are relayed to the caller.
279+
*
280+
* @param action The action to be performed for each element
281+
*/
282+
public void forEach(Consumer<ResolvableType> action) {
283+
roots.forEach(it -> typeCollector.process(it, action));
242284
}
243285

286+
/**
287+
* Return all reachable types as list of {@link Class classes}. The resulting list is unmodifiable.
288+
*
289+
* @return an unmodifiable list of reachable types.
290+
*/
244291
public List<Class<?>> list() {
245292
return reachableTypes.get();
246293
}
247294

248295
private List<Class<?>> collect() {
249296
List<Class<?>> target = new ArrayList<>();
250297
forEach(it -> target.add(it.toClass()));
251-
return target;
298+
return List.copyOf(target);
252299
}
253300
}
254301

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
import org.springframework.data.util.TypeCollector;
2323

2424
/**
25+
* Unit tests for {@link TypeCollector}.
26+
*
2527
* @author Christoph Strobl
28+
* @author Mark Paluch
2629
*/
2730
public class TypeCollectorUnitTests {
2831

@@ -66,4 +69,12 @@ void skipsCoreFrameworkType() {
6669
assertThat(TypeCollector.inspect(org.springframework.core.AliasRegistry.class).list()).isEmpty();
6770
}
6871

72+
@Test // GH-3362
73+
void appliesFilterPredicate() {
74+
assertThat(TypeCollector
75+
.inspect(it -> it.filterTypes(cls -> cls == EmptyType1.class || cls == TypesInMethodSignatures.class),
76+
TypesInMethodSignatures.class)
77+
.list()).containsOnly(TypesInMethodSignatures.class, EmptyType1.class);
78+
}
79+
6980
}

0 commit comments

Comments
 (0)