Skip to content

Commit cfd55be

Browse files
Add AOT repository support
We now use the AOT infrastructure of Spring Framework 6 and data commons to provide AOT support building the foundation for native image compilation. Additionally we register hints for GraalVM native image. See: #4022 Original Pull Request: #4093
1 parent 079c5a9 commit cfd55be

24 files changed

+932
-19
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2022 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+
* https://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+
package org.springframework.data.mongodb;
17+
18+
import java.util.function.Consumer;
19+
20+
import org.springframework.data.domain.ManagedTypes;
21+
22+
/**
23+
* @author Christoph Strobl
24+
* @since 4.0
25+
*/
26+
public final class MongoManagedTypes implements ManagedTypes {
27+
28+
private final ManagedTypes delegate;
29+
30+
public MongoManagedTypes(ManagedTypes types) {
31+
this.delegate = types;
32+
}
33+
34+
public static MongoManagedTypes from(ManagedTypes managedTypes) {
35+
return new MongoManagedTypes(managedTypes);
36+
}
37+
38+
public static MongoManagedTypes of(Iterable<? extends Class<?>> types) {
39+
return from(ManagedTypes.fromIterable(types));
40+
}
41+
42+
public static MongoManagedTypes none() {
43+
return from(ManagedTypes.empty());
44+
}
45+
46+
@Override
47+
public void forEach(Consumer<Class<?>> action) {
48+
delegate.forEach(action);
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2022 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+
* https://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+
package org.springframework.data.mongodb.aot;
17+
18+
import org.springframework.aot.generate.GenerationContext;
19+
import org.springframework.data.aot.AotRepositoryContext;
20+
import org.springframework.data.aot.RepositoryRegistrationAotProcessor;
21+
import org.springframework.data.aot.TypeContributor;
22+
import org.springframework.data.aot.TypeUtils;
23+
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
24+
25+
/**
26+
* @author Christoph Strobl
27+
*/
28+
public class AotMongoRepositoryPostProcessor extends RepositoryRegistrationAotProcessor {
29+
30+
private LazyLoadingProxyAotProcessor lazyLoadingProxyAotProcessor = new LazyLoadingProxyAotProcessor();
31+
32+
@Override
33+
protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) {
34+
// do some custom type registration here
35+
super.contribute(repositoryContext, generationContext);
36+
37+
repositoryContext.getResolvedTypes().stream().filter(MongoAotPredicates.IS_SIMPLE_TYPE.negate()).forEach(type -> {
38+
TypeContributor.contribute(type, it -> true, generationContext);
39+
lazyLoadingProxyAotProcessor.registerLazyLoadingProxyIfNeeded(type, generationContext);
40+
});
41+
}
42+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2022 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+
* https://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+
package org.springframework.data.mongodb.aot;
17+
18+
import java.util.Arrays;
19+
20+
import org.springframework.aot.hint.MemberCategory;
21+
import org.springframework.aot.hint.RuntimeHints;
22+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
23+
import org.springframework.aot.hint.TypeReference;
24+
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
25+
import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback;
26+
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback;
27+
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback;
28+
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository;
29+
import org.springframework.lang.Nullable;
30+
31+
/**
32+
* @author Christoph Strobl
33+
* @since 4.0
34+
*/
35+
public class DataMongoRuntimeHints implements RuntimeHintsRegistrar {
36+
37+
@Override
38+
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
39+
40+
hints.reflection().registerTypes(
41+
Arrays.asList(TypeReference.of(SimpleMongoRepository.class), TypeReference.of(BeforeConvertCallback.class),
42+
TypeReference.of(BeforeSaveCallback.class), TypeReference.of(AfterConvertCallback.class),
43+
TypeReference.of(AfterSaveCallback.class)),
44+
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
45+
MemberCategory.INVOKE_PUBLIC_METHODS));
46+
}
47+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2022 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+
* https://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+
package org.springframework.data.mongodb.aot;
17+
18+
import java.lang.annotation.Annotation;
19+
import java.lang.reflect.Field;
20+
import java.util.ArrayList;
21+
import java.util.LinkedHashSet;
22+
import java.util.List;
23+
import java.util.Set;
24+
25+
import org.springframework.aot.generate.GenerationContext;
26+
import org.springframework.aot.hint.TypeReference;
27+
import org.springframework.core.ResolvableType;
28+
import org.springframework.core.annotation.AnnotatedElementUtils;
29+
import org.springframework.core.annotation.MergedAnnotations;
30+
import org.springframework.data.annotation.Reference;
31+
import org.springframework.data.aot.TypeUtils;
32+
import org.springframework.data.mongodb.core.mapping.DBRef;
33+
import org.springframework.data.mongodb.core.mapping.DocumentReference;
34+
35+
/**
36+
* @author Christoph Strobl
37+
* @since 4.0
38+
*/
39+
public class LazyLoadingProxyAotProcessor {
40+
41+
private boolean generalLazyLoadingProxyContributed = false;
42+
43+
void registerLazyLoadingProxyIfNeeded(Class<?> type, GenerationContext generationContext) {
44+
45+
Set<Field> refFields = getFieldsWithAnnotationPresent(type, Reference.class);
46+
if (refFields.isEmpty()) {
47+
return;
48+
}
49+
50+
refFields.stream() //
51+
.filter(LazyLoadingProxyAotProcessor::isLazyLoading) //
52+
.forEach(field -> {
53+
54+
if (!generalLazyLoadingProxyContributed) {
55+
generationContext.getRuntimeHints().proxies().registerJdkProxy(
56+
TypeReference.of(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class),
57+
TypeReference.of(org.springframework.aop.SpringProxy.class),
58+
TypeReference.of(org.springframework.aop.framework.Advised.class),
59+
TypeReference.of(org.springframework.core.DecoratingProxy.class));
60+
generalLazyLoadingProxyContributed = true;
61+
}
62+
63+
if (field.getType().isInterface()) {
64+
65+
List<Class<?>> interfaces = new ArrayList<>(
66+
TypeUtils.resolveTypesInSignature(ResolvableType.forField(field, type)));
67+
68+
interfaces.add(0, org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class);
69+
interfaces.add(org.springframework.aop.SpringProxy.class);
70+
interfaces.add(org.springframework.aop.framework.Advised.class);
71+
interfaces.add(org.springframework.core.DecoratingProxy.class);
72+
73+
generationContext.getRuntimeHints().proxies().registerJdkProxy(interfaces.toArray(Class[]::new));
74+
} else {
75+
76+
generationContext.getRuntimeHints().proxies().registerClassProxy(field.getType(), builder -> {
77+
builder.proxiedInterfaces(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class);
78+
});
79+
}
80+
});
81+
}
82+
83+
private static boolean isLazyLoading(Field field) {
84+
if (AnnotatedElementUtils.isAnnotated(field, DBRef.class)) {
85+
return AnnotatedElementUtils.findMergedAnnotation(field, DBRef.class).lazy();
86+
}
87+
if (AnnotatedElementUtils.isAnnotated(field, DocumentReference.class)) {
88+
return AnnotatedElementUtils.findMergedAnnotation(field, DocumentReference.class).lazy();
89+
}
90+
return false;
91+
}
92+
93+
private static Set<Field> getFieldsWithAnnotationPresent(Class<?> type, Class<? extends Annotation> annotation) {
94+
95+
Set<Field> fields = new LinkedHashSet<>();
96+
for (Field field : type.getDeclaredFields()) {
97+
if (MergedAnnotations.from(field).get(annotation).isPresent()) {
98+
fields.add(field);
99+
}
100+
}
101+
return fields;
102+
}
103+
104+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2022 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+
* https://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+
package org.springframework.data.mongodb.aot;
17+
18+
import java.util.function.Predicate;
19+
20+
import org.springframework.data.aot.TypeUtils;
21+
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
22+
23+
/**
24+
* @author Christoph Strobl
25+
* @since 4.0
26+
*/
27+
class MongoAotPredicates {
28+
29+
static final Predicate<Class<?>> IS_SIMPLE_TYPE = (type) -> MongoSimpleTypes.HOLDER.isSimpleType(type) || TypeUtils.type(type).isPartOf("org.bson");
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2022 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+
* https://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+
package org.springframework.data.mongodb.aot;
17+
18+
import org.springframework.aot.generate.GenerationContext;
19+
import org.springframework.core.ResolvableType;
20+
import org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor;
21+
import org.springframework.data.mongodb.MongoManagedTypes;
22+
import org.springframework.lang.Nullable;
23+
import org.springframework.util.ClassUtils;
24+
25+
/**
26+
* @author Christoph Strobl
27+
* @since 2022/06
28+
*/
29+
public class MongoManagedTypesBeanRegistrationAotProcessor extends ManagedTypesBeanRegistrationAotProcessor {
30+
31+
private LazyLoadingProxyAotProcessor lazyLoadingProxyAotProcessor = new LazyLoadingProxyAotProcessor();
32+
33+
public MongoManagedTypesBeanRegistrationAotProcessor() {
34+
setModuleIdentifier("mongo");
35+
}
36+
37+
@Override
38+
protected boolean isMatch(@Nullable Class<?> beanType, @Nullable String beanName) {
39+
return isMongoManagedTypes(beanType) || super.isMatch(beanType, beanName);
40+
}
41+
42+
protected boolean isMongoManagedTypes(@Nullable Class<?> beanType) {
43+
return beanType != null && ClassUtils.isAssignable(MongoManagedTypes.class, beanType);
44+
}
45+
46+
@Override
47+
protected void contributeType(ResolvableType type, GenerationContext generationContext) {
48+
49+
if (MongoAotPredicates.IS_SIMPLE_TYPE.test(type.toClass())) {
50+
return;
51+
}
52+
53+
super.contributeType(type, generationContext);
54+
lazyLoadingProxyAotProcessor.registerLazyLoadingProxyIfNeeded(type.toClass(), generationContext);
55+
}
56+
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import java.lang.annotation.Target;
2424

2525
import org.springframework.context.annotation.Import;
26+
import org.springframework.context.annotation.ImportRuntimeHints;
27+
import org.springframework.data.aot.hint.AuditingHints;
2628
import org.springframework.data.auditing.DateTimeProvider;
2729
import org.springframework.data.domain.AuditorAware;
2830

@@ -37,6 +39,7 @@
3739
@Target(ElementType.TYPE)
3840
@Retention(RetentionPolicy.RUNTIME)
3941
@Import(MongoAuditingRegistrar.class)
42+
@ImportRuntimeHints(AuditingHints.AuditingRuntimeHints.class)
4043
public @interface EnableMongoAuditing {
4144

4245
/**

0 commit comments

Comments
 (0)