Skip to content

Commit

Permalink
Drop using FACTORY_BEAN_OBJECT_TYPE attribute entirely.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
odrotbohm committed Aug 2, 2023
1 parent d98eeef commit fe5e80e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -183,9 +184,9 @@ public List<BeanComponentDefinition> 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);
Expand Down Expand Up @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -152,8 +148,6 @@ public RepositoryRegistrationAotContribution forBean(RegisteredBean repositoryBe

this.repositoryContext = buildAotRepositoryContext(repositoryBean, repositoryMetadata);

enhanceRepositoryBeanDefinition(repositoryBean, repositoryMetadata, this.repositoryContext);

return this;
}

Expand Down Expand Up @@ -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<AotRepositoryContext, GenerationContext> moduleContribution) {
this.moduleContribution = moduleContribution;
Expand Down Expand Up @@ -385,8 +347,4 @@ static boolean isJavaOrPrimitiveType(Class<?> type) {
public Predicate<Class<?>> typeFilter() { // like only document ones. // TODO: As in MongoDB?
return it -> true;
}

private static boolean isRepositoryWithTypeParameters(ResolvableType type) {
return type.getGenerics().length == 3;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
});
}
}
Expand Down

0 comments on commit fe5e80e

Please sign in to comment.