DATACMNS-392 - Support for eager repository instantiation.

Changed the initialization of repositories to default to eager with the ability to explicitly defer instantiation using @Lazy on the repository interface definition. In practice, the change in default shouldn't make any difference to most client projects as repositories are usually depended on by other application components so that the repositories never stay uninitialized.
This commit is contained in:
Oliver Gierke
2013-10-27 16:21:24 +01:00
parent d26f90b281
commit e616ec5d05
11 changed files with 131 additions and 32 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-2013 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.
@@ -15,6 +15,7 @@
*/
package org.springframework.data.repository.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -32,22 +33,22 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
private static final String DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX = "Impl";
private final T configurationSource;
private final String interfaceName;
private final BeanDefinition definition;
/**
* Creates a new {@link DefaultRepositoryConfiguration} from the given {@link RepositoryConfigurationSource} and
* interface name.
* source {@link BeanDefinition}.
*
* @param configurationSource must not be {@literal null}.
* @param interfaceName must not be {@literal null} or empty.
* @param definition must not be {@literal null}.
*/
public DefaultRepositoryConfiguration(T configurationSource, String interfaceName) {
public DefaultRepositoryConfiguration(T configurationSource, BeanDefinition definition) {
Assert.notNull(configurationSource);
Assert.hasText(interfaceName);
Assert.notNull(definition);
this.configurationSource = configurationSource;
this.interfaceName = interfaceName;
this.definition = definition;
}
/*
@@ -55,7 +56,7 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
* @see org.springframework.data.repository.config.RepositoryConfiguration#getBeanId()
*/
public String getBeanId() {
return StringUtils.uncapitalize(ClassUtils.getShortName(interfaceName));
return StringUtils.uncapitalize(ClassUtils.getShortName(getRepositoryFactoryBeanName()));
}
/*
@@ -81,7 +82,7 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
* @see org.springframework.data.repository.config.RepositoryConfiguration#getRepositoryInterface()
*/
public String getRepositoryInterface() {
return interfaceName;
return definition.getBeanClassName();
}
/*
@@ -104,7 +105,7 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
* @see org.springframework.data.repository.config.RepositoryConfiguration#getImplementationClassName()
*/
public String getImplementationClassName() {
return ClassUtils.getShortName(interfaceName) + getImplementationPostfix();
return ClassUtils.getShortName(getRepositoryInterface()) + getImplementationPostfix();
}
/*
@@ -148,4 +149,13 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
public String getRepositoryFactoryBeanName() {
return configurationSource.getRepositoryFactoryBeanName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#isLazyInit()
*/
@Override
public boolean isLazyInit() {
return definition.isLazyInit();
}
}

View File

@@ -84,6 +84,7 @@ public class RepositoryBeanDefinitionBuilder {
builder.getRawBeanDefinition().setSource(configuration.getSource());
builder.addPropertyValue("repositoryInterface", configuration.getRepositoryInterface());
builder.addPropertyValue("queryLookupStrategyKey", configuration.getQueryLookupStrategyKey());
builder.addPropertyValue("lazyInit", configuration.isLazyInit());
NamedQueriesBeanDefinitionBuilder definitionBuilder = new NamedQueriesBeanDefinitionBuilder(
extension.getDefaultNamedQueryLocation());

View File

@@ -16,10 +16,14 @@
package org.springframework.data.repository.config;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
@@ -31,6 +35,7 @@ import org.springframework.data.repository.Repository;
import org.springframework.data.repository.RepositoryDefinition;
import org.springframework.data.repository.util.ClassUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Custom {@link ClassPathScanningCandidateComponentProvider} scanning for interfaces extending the given base
@@ -105,6 +110,30 @@ class RepositoryComponentProvider extends ClassPathScanningCandidateComponentPro
return isNonRepositoryInterface && (isTopLevelType || isConsiderNestedRepositories);
}
/**
* Customizes the repository interface detection and triggers annotation detection on them.
*/
@Override
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = super.findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
if (candidate instanceof AnnotatedBeanDefinition) {
// TODO: Remove after upgrade to Spring 3.2.5 - SPR-11032, DATACMNS-386
// Use AnnotationConfigUtils directly
Method method = ReflectionUtils.findMethod(AnnotationConfigUtils.class, "processCommonDefinitionAnnotations",
AnnotatedBeanDefinition.class);
ReflectionUtils.makeAccessible(method);
ReflectionUtils.invokeMethod(method, null, candidate);
}
}
return candidates;
}
/**
* @return the considerNestedRepositoryInterfaces
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-2013 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.
@@ -99,4 +99,11 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
* @return
*/
T getConfigurationSource();
/**
* Returns whether to inititialize the repository proxy lazily.
*
* @return
*/
boolean isLazyInit();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-2013 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.
@@ -21,6 +21,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -50,7 +51,7 @@ public abstract class RepositoryConfigurationExtensionSupport implements Reposit
Set<RepositoryConfiguration<T>> result = new HashSet<RepositoryConfiguration<T>>();
for (String candidate : configSource.getCandidates(loader)) {
for (BeanDefinition candidate : configSource.getCandidates(loader)) {
result.add(getRepositoryConfiguration(candidate, configSource));
}
return result;
@@ -138,12 +139,12 @@ public abstract class RepositoryConfigurationExtensionSupport implements Reposit
* interface name. Defaults to the {@link DefaultRepositoryConfiguration} but allows sub-classes to override this to
* customize the behaviour.
*
* @param interfaceName will never be {@literal null} or empty.
* @param definition will never be {@literal null} or empty.
* @param configSource will never be {@literal null}.
* @return
*/
protected <T extends RepositoryConfigurationSource> RepositoryConfiguration<T> getRepositoryConfiguration(
String interfaceName, T configSource) {
return new DefaultRepositoryConfiguration<T>(configSource, interfaceName);
BeanDefinition definition, T configSource) {
return new DefaultRepositoryConfiguration<T>(configSource, definition);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.repository.config;
import java.util.Collection;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.repository.query.QueryLookupStrategy;
@@ -71,10 +72,10 @@ public interface RepositoryConfigurationSource {
String getRepositoryFactoryBeanName();
/**
* Returns the fully-qualified names of the repository interfaces to create repository instances for.
* Returns the source {@link BeanDefinition}s of the repository interfaces to create repository instances for.
*
* @param loader
* @return
*/
Collection<String> getCandidates(ResourceLoader loader);
Collection<BeanDefinition> getCandidates(ResourceLoader loader);
}

View File

@@ -52,7 +52,7 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfiguration#getCandidates(org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider)
*/
public Collection<String> getCandidates(ResourceLoader loader) {
public Collection<BeanDefinition> getCandidates(ResourceLoader loader) {
RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters());
scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
@@ -63,13 +63,11 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository
scanner.addExcludeFilter(filter);
}
Set<String> result = new HashSet<String>();
Set<BeanDefinition> result = new HashSet<BeanDefinition>();
for (String basePackage : getBasePackages()) {
Collection<BeanDefinition> components = scanner.findCandidateComponents(basePackage);
for (BeanDefinition definition : components) {
result.add(definition.getBeanClassName());
}
Set<BeanDefinition> candidate = scanner.findCandidateComponents(basePackage);
result.addAll(candidate);
}
return result;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2012 the original author or authors.
* Copyright 2008-2013 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.
@@ -52,6 +52,9 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
private NamedQueries namedQueries;
private MappingContext<?, ?> mappingContext;
private ClassLoader classLoader;
private boolean lazyInit = false;
private T repository;
/**
* Setter to inject the repository interface to implement.
@@ -102,6 +105,15 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
this.mappingContext = mappingContext;
}
/**
* Configures whether to initialize the repository proxy lazily. This defaults to {@literal false}.
*
* @param lazyInit whether to initialize the repository proxy lazily. This defaults to {@literal false}.
*/
public void setLazyInit(boolean lazy) {
this.lazyInit = lazy;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
@@ -159,7 +171,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public T getObject() {
return factory.getRepository(repositoryInterface, customImplementation);
return initAndReturn();
}
/*
@@ -189,6 +201,26 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
this.factory.setNamedQueries(namedQueries);
this.factory.setBeanClassLoader(classLoader);
if (!lazyInit) {
initAndReturn();
}
}
/**
* Returns the previously initialized repository proxy or creates and returns the proxy if previously uninitialized.
*
* @return
*/
private T initAndReturn() {
Assert.notNull(repositoryInterface, "Repository interface must not be null on initialization!");
if (this.repository == null) {
this.repository = this.factory.getRepository(repositoryInterface, customImplementation);
}
return this.repository;
}
/**