Skip to content

Commit 54b8baa

Browse files
Add AOT type configuration API
Update tests and add convenience API for registering configurations required to interact with certain types. See: #2595 Original Pull Request: #3318
1 parent 6e60603 commit 54b8baa

19 files changed

+578
-143
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@
2222
import java.util.Optional;
2323
import java.util.Set;
2424
import java.util.function.Consumer;
25+
import java.util.function.Predicate;
2526

2627
import org.jspecify.annotations.Nullable;
28+
import org.springframework.aot.hint.TypeReference;
2729
import org.springframework.beans.factory.BeanFactory;
2830
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2931
import org.springframework.beans.factory.config.BeanDefinition;
3032
import org.springframework.beans.factory.config.BeanReference;
3133
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3234
import org.springframework.beans.factory.support.RootBeanDefinition;
35+
import org.springframework.core.ResolvableType;
3336
import org.springframework.core.env.Environment;
3437
import org.springframework.core.env.EnvironmentCapable;
3538
import org.springframework.core.env.StandardEnvironment;
@@ -227,6 +230,20 @@ default IntrospectedBeanDefinition introspectBeanDefinition(BeanReference refere
227230
*/
228231
IntrospectedBeanDefinition introspectBeanDefinition(String beanName);
229232

233+
InstantiationCreator instantiationCreator(TypeReference typeReference);
234+
235+
default AotTypeConfiguration typeConfiguration(ResolvableType resolvableType) {
236+
return typeConfiguration(resolvableType.toClass());
237+
}
238+
239+
default AotTypeConfiguration typeConfiguration(Class<?> type) {
240+
return typeConfiguration(TypeReference.of(type));
241+
}
242+
243+
AotTypeConfiguration typeConfiguration(TypeReference typeReference);
244+
245+
Collection<AotTypeConfiguration> typeConfigurations();
246+
230247
/**
231248
* Type-based introspector to resolve {@link Class} from a type name and to introspect the bean factory for presence
232249
* of beans.
@@ -286,7 +303,6 @@ default void ifTypePresent(Consumer<Class<?>> action) {
286303
* @return a {@link List} of bean names. The list is empty if the bean factory does not hold any beans of this type.
287304
*/
288305
List<String> getBeanNames();
289-
290306
}
291307

292308
/**
@@ -340,7 +356,11 @@ interface IntrospectedBeanDefinition {
340356
*/
341357
@Nullable
342358
Class<?> resolveType();
359+
}
343360

361+
interface InstantiationCreator {
362+
boolean isAvailable();
363+
void create();
344364
}
345365

346366
}

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.aot;
1717

18+
import org.apache.commons.logging.Log;
19+
import org.apache.commons.logging.LogFactory;
1820
import org.springframework.data.mapping.Association;
1921
import org.springframework.data.mapping.PersistentEntity;
2022
import org.springframework.data.mapping.context.AbstractMappingContext;
@@ -26,6 +28,7 @@
2628
import org.springframework.data.mapping.model.PersistentEntityClassInitializer;
2729
import org.springframework.data.mapping.model.Property;
2830
import org.springframework.data.mapping.model.SimpleTypeHolder;
31+
import org.springframework.data.repository.aot.generate.RepositoryContributor;
2932
import org.springframework.data.util.TypeInformation;
3033

3134
/**
@@ -34,9 +37,12 @@
3437
* @author Mark Paluch
3538
* @since 4.0
3639
*/
37-
public class AotMappingContext extends
40+
class AotMappingContext extends // TODO: hide this one and delegate to other component - can we use the
41+
// AotContext for it?
3842
AbstractMappingContext<BasicPersistentEntity<?, AotMappingContext.BasicPersistentProperty>, AotMappingContext.BasicPersistentProperty> {
3943

44+
private static final Log logger = LogFactory.getLog(AotMappingContext.class);
45+
4046
private final EntityInstantiators instantiators = new EntityInstantiators();
4147
private final ClassGeneratingPropertyAccessorFactory propertyAccessorFactory = new ClassGeneratingPropertyAccessorFactory();
4248

@@ -55,15 +61,18 @@ public void contribute(PersistentEntity<?, ?> entity) {
5561
propertyAccessorFactory.initialize(entity);
5662
}
5763

64+
// TODO: can we extract some util for this using only type
5865
@Override
5966
protected <T> BasicPersistentEntity<?, BasicPersistentProperty> createPersistentEntity(
6067
TypeInformation<T> typeInformation) {
68+
logger.debug("I hate gradle: create persistent entity for type: " + typeInformation);
6169
return new BasicPersistentEntity<>(typeInformation);
6270
}
6371

6472
@Override
6573
protected BasicPersistentProperty createPersistentProperty(Property property,
6674
BasicPersistentEntity<?, BasicPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
75+
logger.info("creating property: " + property.getName());
6776
return new BasicPersistentProperty(property, owner, simpleTypeHolder);
6877
}
6978

@@ -74,10 +83,21 @@ public BasicPersistentProperty(Property property, PersistentEntity<?, BasicPersi
7483
super(property, owner, simpleTypeHolder);
7584
}
7685

86+
@Override
87+
public boolean isAssociation() {
88+
return false;
89+
}
90+
7791
@Override
7892
protected Association<BasicPersistentProperty> createAssociation() {
79-
return null;
93+
return new Association<>(this, null);
8094
}
95+
96+
@Override
97+
public Association<BasicPersistentProperty> getRequiredAssociation() {
98+
return new Association<>(this, null);
99+
}
100+
81101
}
82102

83103
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2025. the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.aot;
18+
19+
import java.io.Serializable;
20+
import java.util.List;
21+
import java.util.function.Predicate;
22+
import java.util.stream.Stream;
23+
24+
import org.springframework.aop.SpringProxy;
25+
import org.springframework.aop.framework.Advised;
26+
import org.springframework.aot.generate.GenerationContext;
27+
import org.springframework.aot.hint.MemberCategory;
28+
import org.springframework.aot.hint.TypeReference;
29+
import org.springframework.core.DecoratingProxy;
30+
import org.springframework.core.env.Environment;
31+
import org.springframework.data.projection.TargetAware;
32+
33+
/**
34+
* @author Christoph Strobl
35+
*/
36+
public interface AotTypeConfiguration {
37+
38+
AotTypeConfiguration forDataBinding();
39+
40+
AotTypeConfiguration forReflectiveAccess(MemberCategory... categories);
41+
42+
AotTypeConfiguration generateEntityInstantiator();
43+
44+
// TODO: ? should this be a global condition for the entire configuration or do we need it for certain aspects ?
45+
AotTypeConfiguration conditional(Predicate<TypeReference> filter);
46+
47+
default AotTypeConfiguration usedAsProjectionInterface() {
48+
return proxyInterface(TargetAware.class, SpringProxy.class, DecoratingProxy.class);
49+
}
50+
51+
default AotTypeConfiguration springProxy() {
52+
return proxyInterface(SpringProxy.class, Advised.class, DecoratingProxy.class);
53+
}
54+
55+
default AotTypeConfiguration repositoryProxy() {
56+
57+
springProxy();
58+
59+
List<TypeReference> transactionalProxy = List.of(TypeReference.of("org.springframework.data.repository.Repository"),
60+
TypeReference.of("org.springframework.transaction.interceptor.TransactionalProxy"),
61+
TypeReference.of("org.springframework.aop.framework.Advised"), TypeReference.of(DecoratingProxy.class));
62+
proxyInterface(transactionalProxy);
63+
64+
proxyInterface(
65+
Stream.concat(transactionalProxy.stream(), Stream.of(TypeReference.of(Serializable.class))).toList());
66+
67+
return this;
68+
}
69+
70+
AotTypeConfiguration proxyInterface(List<TypeReference> proxyInterfaces);
71+
72+
default AotTypeConfiguration proxyInterface(TypeReference... proxyInterfaces) {
73+
return proxyInterface(List.of(proxyInterfaces));
74+
}
75+
76+
default AotTypeConfiguration proxyInterface(Class<?>... proxyInterfaces) {
77+
return proxyInterface(Stream.of(proxyInterfaces).map(TypeReference::of).toList());
78+
}
79+
80+
AotTypeConfiguration forQuerydsl();
81+
82+
void contribute(GenerationContext generationContext);
83+
84+
static Predicate<TypeReference> userConfiguredCondition(Environment environment) {
85+
86+
return new Predicate<TypeReference>() {
87+
88+
private final List<String> allowedAccessorTypes = environment.getProperty("spring.data.aot.generate.accessor",
89+
List.class, List.of());
90+
91+
@Override
92+
@SuppressWarnings("unchecked")
93+
public boolean test(TypeReference typeReference) {
94+
95+
if (!allowedAccessorTypes.isEmpty()) {
96+
if (allowedAccessorTypes.contains("none") || allowedAccessorTypes.contains("false")
97+
|| allowedAccessorTypes.contains("off")) {
98+
return false;
99+
}
100+
if (!allowedAccessorTypes.contains(typeReference.getName())) {
101+
return false;
102+
}
103+
}
104+
105+
return true;
106+
}
107+
};
108+
}
109+
110+
}

0 commit comments

Comments
 (0)