From 98f20a4457ace08348a8e568eaed2c451e127699 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. Any other generics are simply filled with Object.class. Ticket: #2894. --- .../RepositoryConfigurationDelegate.java | 30 +++++++++---- ...RepositoryRegistrationAotContribution.java | 42 ------------------- ...ositoryConfigurationDelegateUnitTests.java | 11 +++-- 3 files changed, 30 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..1fdf2f8401 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; @@ -70,7 +70,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 +182,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 +315,31 @@ 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); + + int numberOfGenerics = factoryBean.getTypeParameters().length; + + Class[] generics = new Class[numberOfGenerics]; + generics[0] = repositoryInterface; + + if (numberOfGenerics > 1) { + for (int i = 1; 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"); }); } }