From e616ec5d05716b84aebb0d4272f2786590327e7b Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Sun, 27 Oct 2013 16:21:24 +0100 Subject: [PATCH] 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. --- .../DefaultRepositoryConfiguration.java | 30 ++++++++++------ .../RepositoryBeanDefinitionBuilder.java | 1 + .../config/RepositoryComponentProvider.java | 29 +++++++++++++++ .../config/RepositoryConfiguration.java | 9 ++++- ...positoryConfigurationExtensionSupport.java | 11 +++--- .../config/RepositoryConfigurationSource.java | 5 +-- .../RepositoryConfigurationSourceSupport.java | 10 +++--- .../support/RepositoryFactoryBeanSupport.java | 36 +++++++++++++++++-- ...epositoryConfigurationSourceUnitTests.java | 7 ++-- ...faultRepositoryConfigurationUnitTests.java | 9 ++--- ...RepositoryFactoryBeanSupportUnitTests.java | 16 +++++++++ 11 files changed, 131 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java b/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java index b27116905..9e70c1322 100644 --- a/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java +++ b/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java @@ -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 findCandidateComponents(String basePackage) { + + Set 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 */ diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java index e778ca517..f9af621de 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java @@ -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> result = new HashSet>(); - 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 RepositoryConfiguration getRepositoryConfiguration( - String interfaceName, T configSource) { - return new DefaultRepositoryConfiguration(configSource, interfaceName); + BeanDefinition definition, T configSource) { + return new DefaultRepositoryConfiguration(configSource, definition); } } diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java index 2e0bbfc49..e8e5a4e4b 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java @@ -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 getCandidates(ResourceLoader loader); + Collection getCandidates(ResourceLoader loader); } diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java index b398763e7..73d92e225 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java @@ -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 getCandidates(ResourceLoader loader) { + public Collection 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 result = new HashSet(); + Set result = new HashSet(); for (String basePackage : getBasePackages()) { - Collection components = scanner.findCandidateComponents(basePackage); - for (BeanDefinition definition : components) { - result.add(definition.getBeanClassName()); - } + Set candidate = scanner.findCandidateComponents(basePackage); + result.addAll(candidate); } return result; diff --git a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java index 2e5214eba..25db19452 100644 --- a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java +++ b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java @@ -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, 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, 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, * @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, 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; } /** diff --git a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java index 6bae75367..07d06671f 100644 --- a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java @@ -22,6 +22,7 @@ import java.util.Collection; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.DefaultResourceLoader; @@ -57,9 +58,11 @@ public class AnnotationRepositoryConfigurationSourceUnitTests { @Test public void evaluatesExcludeFiltersCorrectly() { - Collection candidates = source.getCandidates(new DefaultResourceLoader()); + Collection candidates = source.getCandidates(new DefaultResourceLoader()); assertThat(candidates, hasSize(1)); - assertThat(candidates, hasItem(MyRepository.class.getName())); + + BeanDefinition candidate = candidates.iterator().next(); + assertThat(candidate.getBeanClassName(), is(MyRepository.class.getName())); } @Test diff --git a/src/test/java/org/springframework/data/repository/config/DefaultRepositoryConfigurationUnitTests.java b/src/test/java/org/springframework/data/repository/config/DefaultRepositoryConfigurationUnitTests.java index eb24f3bb7..db51f17ad 100644 --- a/src/test/java/org/springframework/data/repository/config/DefaultRepositoryConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/DefaultRepositoryConfigurationUnitTests.java @@ -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. @@ -22,6 +22,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.data.repository.query.QueryLookupStrategy.Key; /** @@ -32,19 +33,19 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; @RunWith(MockitoJUnitRunner.class) public class DefaultRepositoryConfigurationUnitTests { - @Mock - RepositoryConfigurationSource source; + @Mock RepositoryConfigurationSource source; @Test public void supportsBasicConfiguration() { RepositoryConfiguration configuration = new DefaultRepositoryConfiguration( - source, "com.acme.MyRepository"); + source, new RootBeanDefinition("com.acme.MyRepository")); assertThat(configuration.getConfigurationSource(), is(source)); assertThat(configuration.getImplementationBeanName(), is("myRepositoryImpl")); assertThat(configuration.getImplementationClassName(), is("MyRepositoryImpl")); assertThat(configuration.getRepositoryInterface(), is("com.acme.MyRepository")); assertThat(configuration.getQueryLookupStrategyKey(), is((Object) Key.CREATE_IF_NOT_FOUND)); + assertThat(configuration.isLazyInit(), is(false)); } } diff --git a/src/test/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupportUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupportUnitTests.java index fa39038b6..be0316fb4 100644 --- a/src/test/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupportUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupportUnitTests.java @@ -19,7 +19,9 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.test.util.ReflectionTestUtils; /** @@ -29,6 +31,8 @@ import org.springframework.test.util.ReflectionTestUtils; */ public class RepositoryFactoryBeanSupportUnitTests { + public @Rule ExpectedException exception = ExpectedException.none(); + /** * @see DATACMNS-341 */ @@ -40,9 +44,21 @@ public class RepositoryFactoryBeanSupportUnitTests { RepositoryFactoryBeanSupport factoryBean = new DummyRepositoryFactoryBean(); factoryBean.setBeanClassLoader(classLoader); + factoryBean.setLazyInit(true); factoryBean.afterPropertiesSet(); Object factory = ReflectionTestUtils.getField(factoryBean, "factory"); assertThat(ReflectionTestUtils.getField(factory, "classLoader"), is((Object) classLoader)); } + + @Test + @SuppressWarnings("rawtypes") + public void initializationFailsWithMissingRepositoryInterface() { + + exception.expect(IllegalArgumentException.class); + exception.expectMessage("Repository interface"); + + RepositoryFactoryBeanSupport factoryBean = new DummyRepositoryFactoryBean(); + factoryBean.afterPropertiesSet(); + } }