DATACMNS-526 - Improved configuration behavior in multi-store scenarios.

We now apply a stricter repository interface selection if we detect multiple Spring Data modules to be in the classpath to avoid the repository scanning accidentally overriding each others definitions or even picking up interfaces they weren't intended to manage.

The detection is based on a type scan in dedicated base package where subtypes of RepositoryFactorySupport usually reside. If more than one type is found, we activate strict scanning.

The strict check is actually implemented in RepositoryConfigurationExtensionSupport to be accessible for store implementations. By default we try to load the repository candidate interface and inspect the managed domain types for a collection of annotations (see RepositoryConfigurationExtensionSupport.getIdentifyingAnnotations()). Implementors still have the chance to customize the behavior by overriding isStrictRepositoryCandidate(…).

We also introduced a getModuleName() to be able to create better logging output in terms of repository registration. Moved registration of RepositoryConfigurationExtension as Spring bean into the base types for XML and annotation configuration support to make sure they only kick in if explicit configuration is used.
This commit is contained in:
Oliver Gierke
2014-07-28 19:42:10 +02:00
parent 3bcf6f3f27
commit 81af5ee730
15 changed files with 455 additions and 48 deletions

View File

@@ -23,6 +23,8 @@ import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
@@ -127,16 +129,26 @@ public class AnnotationRepositoryConfigurationSourceUnitTests {
public void returnsEmptyStringForBasePackage() throws Exception {
StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(getClass().getClassLoader().loadClass(
"TypeInDefaultPackage"));
"TypeInDefaultPackage"), true);
RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata,
EnableRepositories.class, resourceLoader, environment);
assertThat(configurationSource.getBasePackages(), hasItem(""));
}
/**
* @see DATACMNS-526
*/
@Test
public void detectsExplicitFilterConfiguration() {
assertThat(getConfigSource(ConfigurationWithExplicitFilter.class).usesExplicitFilters(), is(true));
assertThat(getConfigSource(DefaultConfiguration.class).usesExplicitFilters(), is(false));
}
private AnnotationRepositoryConfigurationSource getConfigSource(Class<?> type) {
AnnotationMetadata metadata = new StandardAnnotationMetadata(type);
AnnotationMetadata metadata = new StandardAnnotationMetadata(type, true);
return new AnnotationRepositoryConfigurationSource(metadata, EnableRepositories.class, resourceLoader, environment);
}
@@ -150,4 +162,7 @@ public class AnnotationRepositoryConfigurationSourceUnitTests {
@EnableRepositories(considerNestedRepositories = true)
static class DefaultConfigurationWithNestedRepositories {}
@EnableRepositories(excludeFilters = { @Filter(Primary.class) })
static class ConfigurationWithExplicitFilter {}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2012-2014 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.
@@ -74,7 +74,7 @@ public class RepositoryComponentProviderUnitTests {
Set<BeanDefinition> components = provider.findCandidateComponents("org.springframework.data.repository.config");
String nestedRepositoryClassName = "org.springframework.data.repository.config.RepositoryComponentProviderUnitTests$MyNestedRepository";
assertThat(components.size(), is(3));
assertThat(components.size(), is(greaterThanOrEqualTo(1)));
assertThat(components,
Matchers.<BeanDefinition> hasItem(hasProperty("beanClassName", is(nestedRepositoryClassName))));
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository.config;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import org.junit.Test;
import org.springframework.context.annotation.Primary;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
/**
* Unit tests for {@link RepositoryConfigurationExtensionSupport}.
*
* @author Oliver Gierke
*/
public class RepositoryConfigurationExtensionSupportUnitTests {
RepositoryConfigurationExtensionSupport extension = new SampleRepositoryConfigurationExtension();
/**
* @see DATACMNS-526
*/
@Test
public void doesNotConsiderRepositoryForPlainTypeStrictMatch() {
assertThat(extension.isStrictRepositoryCandidate(PlainTypeRepository.class), is(false));
}
/**
* @see DATACMNS-526
*/
@Test
public void considersRepositoryWithAnnotatedTypeStrictMatch() {
assertThat(extension.isStrictRepositoryCandidate(AnnotatedTypeRepository.class), is(true));
}
static class SampleRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModulePrefix()
*/
@Override
protected String getModulePrefix() {
return "core";
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtension#getRepositoryFactoryClassName()
*/
@Override
public String getRepositoryFactoryClassName() {
return RepositoryFactorySupport.class.getName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingAnnotations()
*/
@Override
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
return Collections.<Class<? extends Annotation>> singleton(Primary.class);
}
}
@Primary
static class AnnotatedType {}
static class PlainType {}
interface AnnotatedTypeRepository extends Repository<AnnotatedType, Long> {}
interface PlainTypeRepository extends Repository<PlainType, Long> {}
}