From 2b06d452091191c85b640452cba830b73b42a28b Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Mon, 11 Jul 2022 15:22:13 +0200 Subject: [PATCH] Add Spring Framework AOT support. --- .../data/neo4j/aot/Neo4jAotPredicates.java | 28 +++++++ .../data/neo4j/aot/Neo4jManagedTypes.java | 78 +++++++++++++++++++ ...agedTypesBeanRegistrationAotProcessor.java | 53 +++++++++++++ .../data/neo4j/aot/Neo4jRuntimeHints.java | 55 +++++++++++++ .../neo4j/config/Neo4jAuditingRegistrar.java | 17 ++-- ...Neo4jRepositoryConfigurationExtension.java | 1 + .../resources/META-INF/spring/aot.factories | 4 + 7 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/springframework/data/neo4j/aot/Neo4jAotPredicates.java create mode 100644 src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypes.java create mode 100644 src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypesBeanRegistrationAotProcessor.java create mode 100644 src/main/java/org/springframework/data/neo4j/aot/Neo4jRuntimeHints.java create mode 100644 src/main/resources/META-INF/spring/aot.factories diff --git a/src/main/java/org/springframework/data/neo4j/aot/Neo4jAotPredicates.java b/src/main/java/org/springframework/data/neo4j/aot/Neo4jAotPredicates.java new file mode 100644 index 000000000..7e0aea227 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/aot/Neo4jAotPredicates.java @@ -0,0 +1,28 @@ +/* + * Copyright 2011-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.neo4j.aot; + +import org.springframework.data.neo4j.core.convert.Neo4jSimpleTypes; + +import java.util.function.Predicate; + +/** + * @author Gerrit Meier + * @since 7.0.0 + */ +public class Neo4jAotPredicates { + static final Predicate> IS_SIMPLE_TYPE = Neo4jSimpleTypes.HOLDER::isSimpleType; +} diff --git a/src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypes.java b/src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypes.java new file mode 100644 index 000000000..5d561e79f --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypes.java @@ -0,0 +1,78 @@ +/* + * Copyright 2011-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.neo4j.aot; + +import org.springframework.data.domain.ManagedTypes; + +import java.util.Arrays; +import java.util.function.Consumer; + +/** + * @author Gerrit Meier + * @since 7.0.0 + */ +public final class Neo4jManagedTypes implements ManagedTypes { + + private final ManagedTypes delegate; + + private Neo4jManagedTypes(ManagedTypes types) { + this.delegate = types; + } + + /** + * Wraps an existing {@link ManagedTypes} object with {@link Neo4jManagedTypes}. + */ + public static Neo4jManagedTypes from(ManagedTypes managedTypes) { + return new Neo4jManagedTypes(managedTypes); + } + + /** + * Factory method used to construct {@link Neo4jManagedTypes} from the given array of {@link Class types}. + * + * @param types array of {@link Class types} used to initialize the {@link ManagedTypes}; must not be {@literal null}. + * @return new instance of {@link Neo4jManagedTypes} initialized from {@link Class types}. + */ + public static Neo4jManagedTypes from(Class... types) { + return fromIterable(Arrays.asList(types)); + } + + /** + * Factory method used to construct {@link Neo4jManagedTypes} from the given, required {@link Iterable} of + * {@link Class types}. + * + * @param types {@link Iterable} of {@link Class types} used to initialize the {@link ManagedTypes}; must not be + * {@literal null}. + * @return new instance of {@link Neo4jManagedTypes} initialized the given, required {@link Iterable} of {@link Class + * types}. + */ + public static Neo4jManagedTypes fromIterable(Iterable> types) { + return from(ManagedTypes.fromIterable(types)); + } + + /** + * Factory method to return an empty {@link Neo4jManagedTypes} object. + * + * @return an empty {@link Neo4jManagedTypes} object. + */ + public static Neo4jManagedTypes empty() { + return from(ManagedTypes.empty()); + } + + @Override + public void forEach(Consumer> action) { + delegate.forEach(action); + } +} diff --git a/src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypesBeanRegistrationAotProcessor.java b/src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypesBeanRegistrationAotProcessor.java new file mode 100644 index 000000000..506c88287 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/aot/Neo4jManagedTypesBeanRegistrationAotProcessor.java @@ -0,0 +1,53 @@ +/* + * Copyright 2011-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.neo4j.aot; + +import org.springframework.aot.generate.GenerationContext; +import org.springframework.core.ResolvableType; +import org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor; +import org.springframework.data.domain.ManagedTypes; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; + +/** + * @author Gerrit Meier + * @since 7.0.0 + */ +public class Neo4jManagedTypesBeanRegistrationAotProcessor extends ManagedTypesBeanRegistrationAotProcessor { + + public Neo4jManagedTypesBeanRegistrationAotProcessor() { + setModuleIdentifier("neo4j"); + } + + @Override + protected boolean isMatch(@Nullable Class beanType, @Nullable String beanName) { + return isNeo4jManagedTypes(beanType) || super.isMatch(beanType, beanName); + } + + protected boolean isNeo4jManagedTypes(@Nullable Class beanType) { + return beanType != null && ClassUtils.isAssignable(ManagedTypes.class, beanType); + } + + @Override + protected void contributeType(ResolvableType type, GenerationContext generationContext) { + + if (Neo4jAotPredicates.IS_SIMPLE_TYPE.test(type.toClass())) { + return; + } + + super.contributeType(type, generationContext); + } +} diff --git a/src/main/java/org/springframework/data/neo4j/aot/Neo4jRuntimeHints.java b/src/main/java/org/springframework/data/neo4j/aot/Neo4jRuntimeHints.java new file mode 100644 index 000000000..beb65fa6a --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/aot/Neo4jRuntimeHints.java @@ -0,0 +1,55 @@ +/* + * Copyright 2011-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.neo4j.aot; + +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.neo4j.core.mapping.callback.AfterConvertCallback; +import org.springframework.data.neo4j.core.mapping.callback.BeforeBindCallback; +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.support.UUIDStringGenerator; +import org.springframework.data.neo4j.repository.query.SimpleQueryByExampleExecutor; +import org.springframework.data.neo4j.repository.support.SimpleNeo4jRepository; +import org.springframework.lang.Nullable; + +import java.util.Arrays; + +/** + * @author Gerrit Meier + * @since 7.0.0 + */ +public class Neo4jRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + + hints.reflection().registerTypes( + Arrays.asList( + TypeReference.of(SimpleNeo4jRepository.class), + TypeReference.of(SimpleQueryByExampleExecutor.class), + TypeReference.of(BeforeBindCallback.class), + TypeReference.of(AfterConvertCallback.class), + // todo "temporary" fix, should get resolved when defined in @GeneratedValue + TypeReference.of(UUIDStringGenerator.class), + TypeReference.of(GeneratedValue.InternalIdGenerator.class), + TypeReference.of(GeneratedValue.UUIDGenerator.class) + ), + builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_PUBLIC_METHODS)); + } +} diff --git a/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java b/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java index 534b026a9..4d6d94fb2 100644 --- a/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java +++ b/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java @@ -15,8 +15,6 @@ */ package org.springframework.data.neo4j.config; -import java.lang.annotation.Annotation; - import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -24,10 +22,11 @@ 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.PersistentEntities; import org.springframework.data.neo4j.core.mapping.callback.AuditingBeforeBindCallback; import org.springframework.util.Assert; +import java.lang.annotation.Annotation; + /** * @author Michael J. Simons * @soundtrack Iron Maiden - Killers @@ -62,7 +61,7 @@ final class Neo4jAuditingRegistrar extends AuditingBeanDefinitionRegistrarSuppor */ @Override protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition, - BeanDefinitionRegistry registry) { + BeanDefinitionRegistry registry) { Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null"); Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); @@ -86,11 +85,11 @@ final class Neo4jAuditingRegistrar extends AuditingBeanDefinitionRegistrarSuppor BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class); - BeanDefinitionBuilder persistentEntities = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class) - .setFactoryMethod("of"); - persistentEntities.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME); - - builder.addConstructorArgValue(persistentEntities.getBeanDefinition()); return configureDefaultAuditHandlerAttributes(configuration, builder); } + + @Override + public void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration, BeanDefinitionRegistry registry) { + builder.setFactoryMethod("from").addConstructorArgReference("neo4jMappingContext"); + } } diff --git a/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java index e7fe7382f..564f39c9d 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java @@ -135,4 +135,5 @@ public final class Neo4jRepositoryConfigurationExtension extends RepositoryConfi builder.addPropertyReference("neo4jMappingContext", source.getAttribute("neo4jMappingContextRef").orElse(DEFAULT_MAPPING_CONTEXT_BEAN_NAME)); } + } diff --git a/src/main/resources/META-INF/spring/aot.factories b/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 000000000..dc62df8fb --- /dev/null +++ b/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,4 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=\ + org.springframework.data.neo4j.aot.Neo4jRuntimeHints + org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ + org.springframework.data.neo4j.aot.Neo4jManagedTypesBeanRegistrationAotProcessor \ No newline at end of file