diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoManagedTypes.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoManagedTypes.java new file mode 100644 index 000000000..a7eda5fb6 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoManagedTypes.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb; + +import java.util.function.Consumer; + +import org.springframework.data.domain.ManagedTypes; + +/** + * @author Christoph Strobl + * @since 4.0 + */ +public final class MongoManagedTypes implements ManagedTypes { + + private final ManagedTypes delegate; + + public MongoManagedTypes(ManagedTypes types) { + this.delegate = types; + } + + public static MongoManagedTypes from(ManagedTypes managedTypes) { + return new MongoManagedTypes(managedTypes); + } + + public static MongoManagedTypes of(Iterable> types) { + return from(ManagedTypes.fromIterable(types)); + } + + public static MongoManagedTypes none() { + return from(ManagedTypes.empty()); + } + + @Override + public void forEach(Consumer> action) { + delegate.forEach(action); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/AotMongoRepositoryPostProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/AotMongoRepositoryPostProcessor.java new file mode 100644 index 000000000..dbeb9666c --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/AotMongoRepositoryPostProcessor.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import org.springframework.aot.generate.GenerationContext; +import org.springframework.data.aot.AotRepositoryContext; +import org.springframework.data.aot.RepositoryRegistrationAotProcessor; +import org.springframework.data.aot.TypeContributor; +import org.springframework.data.aot.TypeUtils; +import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; + +/** + * @author Christoph Strobl + */ +public class AotMongoRepositoryPostProcessor extends RepositoryRegistrationAotProcessor { + + private LazyLoadingProxyAotProcessor lazyLoadingProxyAotProcessor = new LazyLoadingProxyAotProcessor(); + + @Override + protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) { + // do some custom type registration here + super.contribute(repositoryContext, generationContext); + + repositoryContext.getResolvedTypes().stream().filter(MongoAotPredicates.IS_SIMPLE_TYPE.negate()).forEach(type -> { + TypeContributor.contribute(type, it -> true, generationContext); + lazyLoadingProxyAotProcessor.registerLazyLoadingProxyIfNeeded(type, generationContext); + }); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/DataMongoRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/DataMongoRuntimeHints.java new file mode 100644 index 000000000..1a9e83d70 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/DataMongoRuntimeHints.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import java.util.Arrays; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.TypeReference; +import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback; +import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback; +import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback; +import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback; +import org.springframework.data.mongodb.repository.support.SimpleMongoRepository; +import org.springframework.lang.Nullable; + +/** + * @author Christoph Strobl + * @since 4.0 + */ +public class DataMongoRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + + hints.reflection().registerTypes( + Arrays.asList(TypeReference.of(SimpleMongoRepository.class), TypeReference.of(BeforeConvertCallback.class), + TypeReference.of(BeforeSaveCallback.class), TypeReference.of(AfterConvertCallback.class), + TypeReference.of(AfterSaveCallback.class)), + builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_PUBLIC_METHODS)); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java new file mode 100644 index 000000000..a7f13f147 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java @@ -0,0 +1,104 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.aot.generate.GenerationContext; +import org.springframework.aot.hint.TypeReference; +import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.data.annotation.Reference; +import org.springframework.data.aot.TypeUtils; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.mapping.DocumentReference; + +/** + * @author Christoph Strobl + * @since 4.0 + */ +public class LazyLoadingProxyAotProcessor { + + private boolean generalLazyLoadingProxyContributed = false; + + void registerLazyLoadingProxyIfNeeded(Class type, GenerationContext generationContext) { + + Set refFields = getFieldsWithAnnotationPresent(type, Reference.class); + if (refFields.isEmpty()) { + return; + } + + refFields.stream() // + .filter(LazyLoadingProxyAotProcessor::isLazyLoading) // + .forEach(field -> { + + if (!generalLazyLoadingProxyContributed) { + generationContext.getRuntimeHints().proxies().registerJdkProxy( + TypeReference.of(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class), + TypeReference.of(org.springframework.aop.SpringProxy.class), + TypeReference.of(org.springframework.aop.framework.Advised.class), + TypeReference.of(org.springframework.core.DecoratingProxy.class)); + generalLazyLoadingProxyContributed = true; + } + + if (field.getType().isInterface()) { + + List> interfaces = new ArrayList<>( + TypeUtils.resolveTypesInSignature(ResolvableType.forField(field, type))); + + interfaces.add(0, org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class); + interfaces.add(org.springframework.aop.SpringProxy.class); + interfaces.add(org.springframework.aop.framework.Advised.class); + interfaces.add(org.springframework.core.DecoratingProxy.class); + + generationContext.getRuntimeHints().proxies().registerJdkProxy(interfaces.toArray(Class[]::new)); + } else { + + generationContext.getRuntimeHints().proxies().registerClassProxy(field.getType(), builder -> { + builder.proxiedInterfaces(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class); + }); + } + }); + } + + private static boolean isLazyLoading(Field field) { + if (AnnotatedElementUtils.isAnnotated(field, DBRef.class)) { + return AnnotatedElementUtils.findMergedAnnotation(field, DBRef.class).lazy(); + } + if (AnnotatedElementUtils.isAnnotated(field, DocumentReference.class)) { + return AnnotatedElementUtils.findMergedAnnotation(field, DocumentReference.class).lazy(); + } + return false; + } + + private static Set getFieldsWithAnnotationPresent(Class type, Class annotation) { + + Set fields = new LinkedHashSet<>(); + for (Field field : type.getDeclaredFields()) { + if (MergedAnnotations.from(field).get(annotation).isPresent()) { + fields.add(field); + } + } + return fields; + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java new file mode 100644 index 000000000..0e37c23e0 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import java.util.function.Predicate; + +import org.springframework.data.aot.TypeUtils; +import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; + +/** + * @author Christoph Strobl + * @since 4.0 + */ +class MongoAotPredicates { + + static final Predicate> IS_SIMPLE_TYPE = (type) -> MongoSimpleTypes.HOLDER.isSimpleType(type) || TypeUtils.type(type).isPartOf("org.bson"); + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java new file mode 100644 index 000000000..d70b314ad --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java @@ -0,0 +1,56 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import org.springframework.aot.generate.GenerationContext; +import org.springframework.core.ResolvableType; +import org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor; +import org.springframework.data.mongodb.MongoManagedTypes; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; + +/** + * @author Christoph Strobl + * @since 2022/06 + */ +public class MongoManagedTypesBeanRegistrationAotProcessor extends ManagedTypesBeanRegistrationAotProcessor { + + private LazyLoadingProxyAotProcessor lazyLoadingProxyAotProcessor = new LazyLoadingProxyAotProcessor(); + + public MongoManagedTypesBeanRegistrationAotProcessor() { + setModuleIdentifier("mongo"); + } + + @Override + protected boolean isMatch(@Nullable Class beanType, @Nullable String beanName) { + return isMongoManagedTypes(beanType) || super.isMatch(beanType, beanName); + } + + protected boolean isMongoManagedTypes(@Nullable Class beanType) { + return beanType != null && ClassUtils.isAssignable(MongoManagedTypes.class, beanType); + } + + @Override + protected void contributeType(ResolvableType type, GenerationContext generationContext) { + + if (MongoAotPredicates.IS_SIMPLE_TYPE.test(type.toClass())) { + return; + } + + super.contributeType(type, generationContext); + lazyLoadingProxyAotProcessor.registerLazyLoadingProxyIfNeeded(type.toClass(), generationContext); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java index c1d8b3941..8b0e1279c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java @@ -23,6 +23,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportRuntimeHints; +import org.springframework.data.aot.hint.AuditingHints; import org.springframework.data.auditing.DateTimeProvider; import org.springframework.data.domain.AuditorAware; @@ -37,6 +39,7 @@ import org.springframework.data.domain.AuditorAware; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(MongoAuditingRegistrar.class) +@ImportRuntimeHints(AuditingHints.AuditingRuntimeHints.class) public @interface EnableMongoAuditing { /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingRegistrar.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingRegistrar.java index aef2be4e2..0dd5c0eba 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingRegistrar.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingRegistrar.java @@ -18,18 +18,18 @@ package org.springframework.data.mongodb.config; import java.lang.annotation.Annotation; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.Ordered; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.auditing.IsNewAwareAuditingHandler; import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport; import org.springframework.data.auditing.config.AuditingConfiguration; import org.springframework.data.config.ParsingUtils; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback; import org.springframework.util.Assert; @@ -40,7 +40,7 @@ import org.springframework.util.Assert; * @author Oliver Gierke * @author Mark Paluch */ -class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { +class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport implements Ordered { @Override protected Class getAnnotation() { @@ -52,12 +52,34 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { return "mongoAuditingHandler"; } + String persistentEntitiesBeanName; + @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null"); Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); + if(persistentEntitiesBeanName == null) { + if (registry instanceof DefaultListableBeanFactory beanFactory) { + for (String bn : beanFactory.getBeanNamesForType(PersistentEntities.class)) { + if (bn.startsWith("mongo")) { + persistentEntitiesBeanName = bn; + } + } + } + if(persistentEntitiesBeanName == null) { + + persistentEntitiesBeanName = BeanDefinitionReaderUtils.uniqueBeanName("mongo.persistent-entities", registry); + + BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class) + .setFactoryMethod("of") + //.addConstructorArgValue(new RuntimeBeanReference(MongoMappingContext.class)) + .addConstructorArgReference("mongoMappingContext"); + registry.registerBeanDefinition(persistentEntitiesBeanName, definition.getBeanDefinition()); + } + } + super.registerBeanDefinitions(annotationMetadata, registry); } @@ -67,11 +89,7 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { Assert.notNull(configuration, "AuditingConfiguration must not be null"); BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class); - - BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(org.springframework.data.repository.config.PersistentEntitiesFactoryBean.class); - definition.addConstructorArgValue(new RuntimeBeanReference(MappingContext.class)); - - builder.addConstructorArgValue(definition.getBeanDefinition()); + builder.addConstructorArgReference(persistentEntitiesBeanName); return configureDefaultAuditHandlerAttributes(configuration, builder); } @@ -91,4 +109,9 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { AuditingEntityCallback.class.getName(), registry); } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java index 2da298468..bc8ba5a93 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java @@ -27,9 +27,11 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen import org.springframework.core.convert.converter.Converter; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.convert.CustomConversions; +import org.springframework.data.domain.ManagedTypes; import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy; import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; +import org.springframework.data.mongodb.MongoManagedTypes; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter; import org.springframework.data.mongodb.core.mapping.Document; @@ -79,11 +81,11 @@ public abstract class MongoConfigurationSupport { * @throws ClassNotFoundException */ @Bean - public MongoMappingContext mongoMappingContext(MongoCustomConversions customConversions) + public MongoMappingContext mongoMappingContext(MongoCustomConversions customConversions, ManagedTypes managedTypes) throws ClassNotFoundException { MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setInitialEntitySet(getInitialEntitySet()); + mappingContext.setManagedTypes(managedTypes); mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); mappingContext.setFieldNamingStrategy(fieldNamingStrategy()); mappingContext.setAutoIndexCreation(autoIndexCreation()); @@ -91,6 +93,16 @@ public abstract class MongoConfigurationSupport { return mappingContext; } + /** + * @return new instance of {@link MongoManagedTypes}. + * @throws ClassNotFoundException + * @since 4.0 + */ + @Bean + public MongoManagedTypes managedTypes() throws ClassNotFoundException { + return MongoManagedTypes.of(getInitialEntitySet()); + } + /** * Register custom {@link Converter}s in a {@link CustomConversions} object if required. These * {@link CustomConversions} will be registered with the diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java index f4fc367e3..1a393a902 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java @@ -27,12 +27,12 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.aop.framework.ProxyFactory; import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.Factory; import org.springframework.cglib.proxy.MethodProxy; +import org.springframework.core.NativeDetector; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.ClientSessionException; @@ -71,6 +71,16 @@ class LazyLoadingProxyFactory { if (!propertyType.isInterface()) { + if (NativeDetector.inNativeImage()) { + + ProxyFactory factory = new ProxyFactory(); + factory.addAdvice(interceptor); + factory.addInterface(LazyLoadingProxy.class); + factory.setTargetClass(propertyType); + factory.setProxyTargetClass(true); + return factory.getProxy(propertyType.getClassLoader()); + } + Factory factory = (Factory) objenesis.newInstance(getEnhancedTypeFor(propertyType)); factory.setCallbacks(new Callback[] { interceptor }); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java index fe4e57fb8..c3a929b01 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java @@ -75,6 +75,15 @@ public abstract class MongoSimpleTypes { simpleTypes.add(UUID.class); simpleTypes.add(Instant.class); + simpleTypes.add(BsonValue.class); + simpleTypes.add(BsonNumber.class); + simpleTypes.add(BsonType.class); + simpleTypes.add(BsonArray.class); + simpleTypes.add(BsonSymbol.class); + simpleTypes.add(BsonUndefined.class); + simpleTypes.add(BsonMinKey.class); + simpleTypes.add(BsonMaxKey.class); + simpleTypes.add(BsonNull.class); simpleTypes.add(BsonBinary.class); simpleTypes.add(BsonBoolean.class); simpleTypes.add(BsonDateTime.class); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtension.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtension.java index 1407294a5..8a8f916b4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtension.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtension.java @@ -19,9 +19,11 @@ import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Collections; +import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.data.config.ParsingUtils; +import org.springframework.data.mongodb.aot.AotMongoRepositoryPostProcessor; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; @@ -29,6 +31,7 @@ import org.springframework.data.repository.config.AnnotationRepositoryConfigurat import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; import org.springframework.data.repository.config.XmlRepositoryConfigurationSource; import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.lang.NonNull; import org.w3c.dom.Element; /** @@ -48,7 +51,7 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati } @Override - protected String getModulePrefix() { + public String getModulePrefix() { return "mongo"; } @@ -62,10 +65,15 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati } @Override - protected Collection> getIdentifyingAnnotations() { + public Collection> getIdentifyingAnnotations() { return Collections.singleton(Document.class); } + @Override + public Class getRepositoryAotProcessor() { + return AotMongoRepositoryPostProcessor.class; + } + @Override protected Collection> getIdentifyingTypes() { return Collections.singleton(MongoRepository.class); diff --git a/spring-data-mongodb/src/main/resources/META-INF/spring/aot.factories b/spring-data-mongodb/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 000000000..03224dba3 --- /dev/null +++ b/spring-data-mongodb/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,4 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=\ + org.springframework.data.mongodb.aot.DataMongoRuntimeHints +org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ + org.springframework.data.mongodb.aot.MongoManagedTypesBeanRegistrationAotProcessor diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/AotMongoRepositoryPostProcessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/AotMongoRepositoryPostProcessorUnitTests.java new file mode 100644 index 000000000..ba83c84a0 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/AotMongoRepositoryPostProcessorUnitTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.mongodb.aot.RepositoryRegistrationAotContributionAssert.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.RegisteredBean; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.annotation.SynthesizedAnnotation; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.annotation.Transient; +import org.springframework.data.aot.RepositoryRegistrationAotContribution; +import org.springframework.data.mongodb.aot.configs.ImperativeConfig; +import org.springframework.data.mongodb.core.convert.LazyLoadingProxy; +import org.springframework.data.mongodb.core.mapping.Address; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * @author Christoph Strobl + * @since 2022/04 + */ +public class AotMongoRepositoryPostProcessorUnitTests { + + @Test + void contributesProxiesForDataAnnotations() { + + RepositoryRegistrationAotContribution repositoryBeanContribution = computeConfiguration(ImperativeConfig.class) + .forRepository(ImperativeConfig.PersonRepository.class); + + assertThatContribution(repositoryBeanContribution) // + .codeContributionSatisfies(contribution -> { + + contribution.contributesJdkProxy(Transient.class, SynthesizedAnnotation.class); + contribution.contributesJdkProxy(LastModifiedDate.class, SynthesizedAnnotation.class); + contribution.contributesJdkProxy(Document.class, SynthesizedAnnotation.class); + contribution.contributesJdkProxy(DBRef.class, SynthesizedAnnotation.class); +// TODO: not supported yet contribution.contributesClassProxy(Address.class, LazyLoadingProxy.class); + }); + } + + BeanContributionBuilder computeConfiguration(Class configuration) { + + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(configuration); + ctx.refreshForAotProcessing(); + + return it -> { + + String[] repoBeanNames = ctx.getBeanNamesForType(it); + assertThat(repoBeanNames).describedAs("Unable to find repository %s in configuration %s.", it, configuration) + .hasSize(1); + + String beanName = repoBeanNames[0]; + + AotMongoRepositoryPostProcessor postProcessor = ctx.getBean(AotMongoRepositoryPostProcessor.class); + + postProcessor.setBeanFactory(ctx.getDefaultListableBeanFactory()); + + BeanRegistrationAotContribution beanRegistrationAotContribution = postProcessor + .processAheadOfTime(RegisteredBean.of(ctx.getBeanFactory(), beanName)); + assertThat(beanRegistrationAotContribution).isInstanceOf(RepositoryRegistrationAotContribution.class); + return (RepositoryRegistrationAotContribution) beanRegistrationAotContribution; + }; + } + + interface BeanContributionBuilder { + RepositoryRegistrationAotContribution forRepository(Class repositoryInterface); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/ClassProxyAssert.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/ClassProxyAssert.java new file mode 100644 index 000000000..0f9fd8261 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/ClassProxyAssert.java @@ -0,0 +1,45 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.assertj.core.api.AbstractAssert; +import org.springframework.aot.hint.ClassProxyHint; +import org.springframework.aot.hint.TypeReference; + +/** + * @author Christoph Strobl + * @since 2022/04 + */ +public class ClassProxyAssert extends AbstractAssert { + + protected ClassProxyAssert(ClassProxyHint classProxyHint) { + super(classProxyHint, ClassProxyAssert.class); + } + + public void matches(Class... proxyInterfaces) { + assertThat(actual.getProxiedInterfaces().stream().map(TypeReference::getCanonicalName)) + .containsExactly(Arrays.stream(proxyInterfaces).map(Class::getCanonicalName).toArray(String[]::new)); + } + + public List getProxiedInterfaces() { + return actual.getProxiedInterfaces(); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/CodeContributionAssert.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/CodeContributionAssert.java new file mode 100644 index 000000000..0b70c770a --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/CodeContributionAssert.java @@ -0,0 +1,120 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.stream.Stream; + +import org.assertj.core.api.AbstractAssert; +import org.springframework.aot.generate.GenerationContext; +import org.springframework.aot.hint.ClassProxyHint; +import org.springframework.aot.hint.JdkProxyHint; +import org.springframework.aot.hint.RuntimeHintsPredicates; + +/** + * @author Christoph Strobl + * @since 2022/04 + */ +public class CodeContributionAssert extends AbstractAssert { + + public CodeContributionAssert(GenerationContext contribution) { + super(contribution, CodeContributionAssert.class); + } + + public CodeContributionAssert contributesReflectionFor(Class... types) { + + for (Class type : types) { + assertThat(this.actual.getRuntimeHints()) + .describedAs("No reflection entry found for [%s]", type) + .matches(RuntimeHintsPredicates.reflection().onType(type)); + } + + return this; + } + + public CodeContributionAssert doesNotContributeReflectionFor(Class... types) { + + for (Class type : types) { + assertThat(this.actual.getRuntimeHints()) + .describedAs("Reflection entry found for [%s]", type) + .matches(RuntimeHintsPredicates.reflection().onType(type).negate()); + } + + return this; + } + + public CodeContributionAssert contributesJdkProxyFor(Class entryPoint) { + + assertThat(jdkProxiesFor(entryPoint).findFirst()) + .describedAs("No JDK proxy found for [%s]", entryPoint) + .isPresent(); + + return this; + } + + public CodeContributionAssert doesNotContributeJdkProxyFor(Class entryPoint) { + + assertThat(jdkProxiesFor(entryPoint).findFirst()) + .describedAs("Found JDK proxy matching [%s] though it should not be present", entryPoint) + .isNotPresent(); + + return this; + } + + public CodeContributionAssert contributesJdkProxy(Class... proxyInterfaces) { + + assertThat(jdkProxiesFor(proxyInterfaces[0])) + .describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces)) + .anySatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces)); + + return this; + } + + public CodeContributionAssert doesNotContributeJdkProxy(Class... proxyInterfaces) { + + assertThat(jdkProxiesFor(proxyInterfaces[0])) + .describedAs("Found JDK proxy matching [%s] though it should not be present", + Arrays.asList(proxyInterfaces)) + .noneSatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces)); + + return this; + } + + private Stream jdkProxiesFor(Class entryPoint) { + + return this.actual.getRuntimeHints().proxies().jdkProxies() + .filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName() + .equals(entryPoint.getCanonicalName())); + } + + public CodeContributionAssert contributesClassProxy(Class... proxyInterfaces) { + + assertThat(classProxiesFor(proxyInterfaces[0])) + .describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces)) + .anySatisfy(it -> new ClassProxyAssert(it).matches(proxyInterfaces)); + + return this; + } + + private Stream classProxiesFor(Class entryPoint) { + + return this.actual.getRuntimeHints().proxies().classProxies() + .filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName() + .equals(entryPoint.getCanonicalName())); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/JdkProxyAssert.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/JdkProxyAssert.java new file mode 100644 index 000000000..cbb81c0da --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/JdkProxyAssert.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.assertj.core.api.AbstractAssert; +import org.springframework.aot.hint.JdkProxyHint; +import org.springframework.aot.hint.TypeReference; + +/** + * @author Christoph Strobl + * @since 2022/04 + */ +public class JdkProxyAssert extends AbstractAssert { + + public JdkProxyAssert(JdkProxyHint jdkProxyHint) { + super(jdkProxyHint, JdkProxyAssert.class); + } + + public void matches(Class... proxyInterfaces) { + assertThat(actual.getProxiedInterfaces().stream().map(TypeReference::getCanonicalName)) + .containsExactly(Arrays.stream(proxyInterfaces).map(Class::getCanonicalName).toArray(String[]::new)); + } + + public List getProxiedInterfaces() { + return actual.getProxiedInterfaces(); + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/RepositoryRegistrationAotContributionAssert.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/RepositoryRegistrationAotContributionAssert.java new file mode 100644 index 000000000..92f8bb10a --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/RepositoryRegistrationAotContributionAssert.java @@ -0,0 +1,111 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.Consumer; + +import org.assertj.core.api.AbstractAssert; +import org.springframework.aot.generate.ClassNameGenerator; +import org.springframework.aot.generate.DefaultGenerationContext; +import org.springframework.aot.generate.InMemoryGeneratedFiles; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; +import org.springframework.beans.factory.aot.BeanRegistrationCode; +import org.springframework.data.aot.RepositoryRegistrationAotContribution; +import org.springframework.data.repository.core.RepositoryInformation; +import org.springframework.data.repository.core.support.RepositoryFragment; +import org.springframework.lang.NonNull; + +/** + * @author Christoph Strobl + * @since 2022/04 + */ +public class RepositoryRegistrationAotContributionAssert + extends AbstractAssert { + + @NonNull + public static RepositoryRegistrationAotContributionAssert assertThatContribution( + @NonNull RepositoryRegistrationAotContribution actual) { + + return new RepositoryRegistrationAotContributionAssert(actual); + } + + public RepositoryRegistrationAotContributionAssert(@NonNull RepositoryRegistrationAotContribution actual) { + super(actual, RepositoryRegistrationAotContributionAssert.class); + } + + public RepositoryRegistrationAotContributionAssert targetRepositoryTypeIs(Class expected) { + + assertThat(getRepositoryInformation().getRepositoryInterface()).isEqualTo(expected); + + return this.myself; + } + + public RepositoryRegistrationAotContributionAssert hasNoFragments() { + + assertThat(getRepositoryInformation().getFragments()).isEmpty(); + + return this; + } + + public RepositoryRegistrationAotContributionAssert hasFragments() { + + assertThat(getRepositoryInformation().getFragments()).isNotEmpty(); + + return this; + } + + public RepositoryRegistrationAotContributionAssert verifyFragments(Consumer>> consumer) { + + assertThat(getRepositoryInformation().getFragments()) + .satisfies(it -> consumer.accept(new LinkedHashSet<>(it))); + + return this; + } + + public RepositoryRegistrationAotContributionAssert codeContributionSatisfies( + Consumer assertWith) { + + BeanRegistrationCode mockBeanRegistrationCode = mock(BeanRegistrationCode.class); + + DefaultGenerationContext generationContext = + new DefaultGenerationContext(new ClassNameGenerator(), new InMemoryGeneratedFiles(), new RuntimeHints()); + + this.actual.applyTo(generationContext, mockBeanRegistrationCode); + + assertWith.accept(new CodeContributionAssert(generationContext)); + + return this; + } + + private RepositoryInformation getRepositoryInformation() { + + assertThat(this.actual) + .describedAs("No repository interface found on null bean contribution") + .isNotNull(); + + assertThat(this.actual.getRepositoryInformation()) + .describedAs("No repository interface found on null repository information") + .isNotNull(); + + return this.actual.getRepositoryInformation(); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/configs/ImperativeConfig.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/configs/ImperativeConfig.java new file mode 100644 index 000000000..0ab6965f2 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/configs/ImperativeConfig.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot.configs; + +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.data.mongodb.aot.domaintypes.Person; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.data.repository.CrudRepository; + +/** + * @author Christoph Strobl + */ +@Configuration +@EnableMongoRepositories(considerNestedRepositories = true, + includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*PersonRepository$") }) +public class ImperativeConfig { + + public interface PersonRepository extends CrudRepository { + + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/domaintypes/Address.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/domaintypes/Address.java new file mode 100644 index 000000000..24d7b8aa8 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/domaintypes/Address.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot.domaintypes; + +import org.springframework.data.mongodb.core.mapping.Field; + +/** + * @author Christoph Strobl + * @since 2022/04 + */ +public class Address { + + @Field("the-street") + String street; + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/domaintypes/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/domaintypes/Person.java new file mode 100644 index 000000000..0d853d4f7 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/domaintypes/Person.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.aot.domaintypes; + +import java.time.Instant; + +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.annotation.Transient; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * @author Christoph Strobl + * @since 2022/04 + */ +@Document("persons") +public class Person { + + @Id String id; + @DBRef(lazy = true) Address address; + + @Transient String transientProperty; + + @LastModifiedDate Instant modifiedAt; +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java index ab65280a1..f1c6c1af1 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java @@ -31,7 +31,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.data.domain.ManagedTypes; import org.springframework.data.mongodb.MongoDatabaseFactory; +import org.springframework.data.mongodb.MongoManagedTypes; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.springframework.data.mongodb.core.convert.MongoTypeMapper; @@ -91,7 +93,7 @@ public class AbstractMongoConfigurationUnitTests { public void returnsUninitializedMappingContext() throws Exception { SampleMongoConfiguration configuration = new SampleMongoConfiguration(); - MongoMappingContext context = configuration.mongoMappingContext(configuration.customConversions()); + MongoMappingContext context = configuration.mongoMappingContext(configuration.customConversions(), MongoManagedTypes.of(Collections.singleton(Entity.class))); assertThat(context.getPersistentEntities()).isEmpty(); context.initialize(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java index 33a2f6110..ce11b2dbf 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java @@ -32,6 +32,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.data.mongodb.MongoManagedTypes; import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -92,7 +93,7 @@ public class AbstractReactiveMongoConfigurationUnitTests { public void returnsUninitializedMappingContext() throws Exception { SampleMongoConfiguration configuration = new SampleMongoConfiguration(); - MongoMappingContext context = configuration.mongoMappingContext(configuration.customConversions()); + MongoMappingContext context = configuration.mongoMappingContext(configuration.customConversions(), MongoManagedTypes.of(Collections.singleton(Entity.class))); assertThat(context.getPersistentEntities()).isEmpty(); context.initialize(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java index 3a08d9b4c..7c0c87700 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java @@ -105,9 +105,7 @@ public class MappingMongoConverterParserIntegrationTests { @Test // DATAMONGO-892 void shouldThrowBeanDefinitionParsingExceptionIfConverterDefinedAsNestedBean() { - assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(this::loadNestedBeanConfiguration); - } @Test // DATAMONGO-925, DATAMONGO-928