From fe5e80e2f461b2eed65b6070710c77a14f2abad0 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 2 Aug 2023 13:51:06 +0200 Subject: [PATCH] Drop using FACTORY_BEAN_OBJECT_TYPE attribute entirely. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now constantly use RootBeanDefinition.setBeanClass(…) (for the raw factory type) and ….setTargetType(…) to declare the full repository factory type including the user defined repository, domain and identifier type. Ticket: #2894. --- .../RepositoryConfigurationDelegate.java | 35 ++++++++++++---- ...RepositoryRegistrationAotContribution.java | 42 ------------------- ...ositoryConfigurationDelegateUnitTests.java | 11 +++-- 3 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java index 8e3258338d..485c37ea7b 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -25,19 +25,19 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.parsing.BeanComponentDefinition; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AutowireCandidateResolver; 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.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.env.StandardEnvironment; @@ -46,6 +46,8 @@ import org.springframework.core.log.LogMessage; import org.springframework.core.metrics.ApplicationStartup; import org.springframework.core.metrics.StartupStep; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.AbstractRepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.util.ReflectionUtils; import org.springframework.lang.Nullable; @@ -70,7 +72,6 @@ public class RepositoryConfigurationDelegate { private static final String REPOSITORY_REGISTRATION = "Spring Data %s - Registering repository: %s - Interface: %s - Factory: %s"; private static final String MULTIPLE_MODULES = "Multiple Spring Data modules found, entering strict repository configuration mode"; private static final String NON_DEFAULT_AUTOWIRE_CANDIDATE_RESOLVER = "Non-default AutowireCandidateResolver (%s) detected. Skipping the registration of LazyRepositoryInjectionPointResolver. Lazy repository injection will not be working"; - private static final String FACTORY_BEAN_OBJECT_TYPE = FactoryBean.OBJECT_TYPE_ATTRIBUTE; private static final Log logger = LogFactory.getLog(RepositoryConfigurationDelegate.class); @@ -183,9 +184,9 @@ public List registerRepositoriesIn(BeanDefinitionRegist extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource); } - AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition(); + RootBeanDefinition beanDefinition = (RootBeanDefinition) definitionBuilder.getBeanDefinition(); - beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, getRepositoryInterface(configuration)); + beanDefinition.setTargetType(getRepositoryInterface(configuration)); beanDefinition.setResourceDescription(configuration.getResourceDescription()); String beanName = configurationSource.generateBeanName(beanDefinition); @@ -316,14 +317,34 @@ private static ApplicationStartup getStartup(BeanDefinitionRegistry registry) { * @return can be {@literal null}. */ @Nullable - private Class getRepositoryInterface(RepositoryConfiguration configuration) { + private ResolvableType getRepositoryInterface(RepositoryConfiguration configuration) { String interfaceName = configuration.getRepositoryInterface(); ClassLoader classLoader = resourceLoader.getClassLoader() == null ? ClassUtils.getDefaultClassLoader() : resourceLoader.getClassLoader(); - return ReflectionUtils.loadIfPresent(interfaceName, classLoader); + classLoader = classLoader != null ? classLoader : getClass().getClassLoader(); + + Class repositoryInterface = ReflectionUtils.loadIfPresent(interfaceName, classLoader); + Class factoryBean = ReflectionUtils.loadIfPresent(configuration.getRepositoryFactoryBeanClassName(), + classLoader); + RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface); + + int numberOfGenerics = factoryBean.getTypeParameters().length; + + Class[] generics = new Class[numberOfGenerics]; + generics[0] = metadata.getRepositoryInterface(); + generics[1] = metadata.getDomainType(); + generics[2] = metadata.getIdType(); + + if (numberOfGenerics > 3) { + for (int i = 3; i < numberOfGenerics; i++) { + generics[i] = Object.class; + } + } + + return ResolvableType.forClassWithGenerics(factoryBean, generics); } /** diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryRegistrationAotContribution.java b/src/main/java/org/springframework/data/repository/config/RepositoryRegistrationAotContribution.java index c01cd3ce2c..a15b8c0b51 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryRegistrationAotContribution.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryRegistrationAotContribution.java @@ -32,21 +32,17 @@ import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.TypeReference; -import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; import org.springframework.beans.factory.aot.BeanRegistrationCode; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.DecoratingProxy; -import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.aot.AotContext; import org.springframework.data.projection.EntityProjectionIntrospector; import org.springframework.data.projection.TargetAware; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryInformation; -import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFragment; import org.springframework.data.util.QTypeContributor; import org.springframework.data.util.TypeContributor; @@ -152,8 +148,6 @@ public RepositoryRegistrationAotContribution forBean(RegisteredBean repositoryBe this.repositoryContext = buildAotRepositoryContext(repositoryBean, repositoryMetadata); - enhanceRepositoryBeanDefinition(repositoryBean, repositoryMetadata, this.repositoryContext); - return this; } @@ -203,44 +197,12 @@ private RepositoryInformation resolveRepositoryInformation(RepositoryConfigurati return RepositoryBeanDefinitionReader.readRepositoryInformation(repositoryMetadata, getBeanFactory()); } - /** - * Helps the AOT processing render the {@link FactoryBean} type correctly that is used to tell the outcome of the - * {@link FactoryBean}. We just need to set the target {@link Repository} {@link Class type} of the - * {@link RepositoryFactoryBeanSupport} while keeping the actual ID and DomainType set to {@link Object}. If the - * generic type signature does not match, then we do not try to resolve and remap the types, but rather set the - * {@literal factoryBeanObjectType} attribute on the {@link RootBeanDefinition}. - */ - protected void enhanceRepositoryBeanDefinition(RegisteredBean repositoryBean, - RepositoryConfiguration repositoryMetadata, AotRepositoryContext repositoryContext) { - - logTrace(String.format("Enhancing repository factory bean definition [%s]", repositoryBean.getBeanName())); - - Class repositoryFactoryBeanType = repositoryContext - .introspectType(repositoryMetadata.getRepositoryFactoryBeanClassName()).resolveType() - .orElse(RepositoryFactoryBeanSupport.class); - - ResolvableType resolvedRepositoryFactoryBeanType = ResolvableType.forClass(repositoryFactoryBeanType); - - RootBeanDefinition repositoryBeanDefinition = repositoryBean.getMergedBeanDefinition(); - - if (isRepositoryWithTypeParameters(resolvedRepositoryFactoryBeanType)) { - repositoryBeanDefinition.setTargetType(ResolvableType.forClassWithGenerics(repositoryFactoryBeanType, - repositoryContext.getRepositoryInformation().getRepositoryInterface(), Object.class, Object.class)); - } else { - repositoryBeanDefinition.setTargetType(resolvedRepositoryFactoryBeanType); - repositoryBeanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, - repositoryContext.getRepositoryInformation().getRepositoryInterface()); - } - } - /** * {@link BiConsumer Callback} for data module specific contributions. * * @param moduleContribution {@link BiConsumer} used by data modules to submit contributions; can be {@literal null}. * @return this. */ - - @SuppressWarnings("unused") public RepositoryRegistrationAotContribution withModuleContribution( @Nullable BiConsumer moduleContribution) { this.moduleContribution = moduleContribution; @@ -385,8 +347,4 @@ static boolean isJavaOrPrimitiveType(Class type) { public Predicate> typeFilter() { // like only document ones. // TODO: As in MongoDB? return it -> true; } - - private static boolean isRepositoryWithTypeParameters(ResolvableType type) { - return type.getGenerics().length == 3; - } } diff --git a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java index 5108ec0170..eb56b208b3 100644 --- a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java @@ -25,8 +25,8 @@ import org.mockito.quality.Strictness; import org.springframework.aop.framework.Advised; import org.springframework.aot.hint.RuntimeHints; -import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; @@ -76,10 +76,13 @@ void registersRepositoryBeanNameAsAttribute() { for (var definition : delegate.registerRepositoriesIn(context, extension)) { var beanDefinition = definition.getBeanDefinition(); - var attribute = beanDefinition.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE); - assertThat(attribute).isInstanceOfSatisfying(Class.class, it -> { - assertThat(it.getName()).endsWith("Repository"); + assertThat(beanDefinition).isInstanceOfSatisfying(RootBeanDefinition.class, it -> { + + var type = it.getTargetType(); + + assertThat(type).isNotNull(); + assertThat(type.getName()).endsWith("FactoryBean"); }); } }