DATACMNS-47 - Guard against @EnableRepositories annotations not carrying @Inherited.

If an @EnableRepository annotation does not carry an @Inherited annotation and the annotation is used in a JavaConfig inheritance scenario the ImportBeanDefinitionRegistrar gets invoked without the annotation metadata available. The RepositoryBeanDefinitionRegistrarSupport now guards against that case and will not invoke registration of repository bean definitions. It's strongly recommended to equip @EnableRepository annotations with @Inherited to accommodate this scenario.
This commit is contained in:
Oliver Gierke
2012-08-08 18:58:01 +02:00
parent 8d156fb591
commit 27a8a8df91
7 changed files with 247 additions and 101 deletions

View File

@@ -42,6 +42,11 @@ public abstract class RepositoryBeanDefinitionRegistrarSupport implements Import
Assert.notNull(annotationMetadata);
Assert.notNull(registry);
// Guard against calls for sub-classes
if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {
return;
}
ResourceLoader resourceLoader = new DefaultResourceLoader();
AnnotationRepositoryConfigurationSource configuration = new AnnotationRepositoryConfigurationSource(
annotationMetadata, getAnnotation());

View File

@@ -15,13 +15,18 @@
*/
package org.springframework.data.repository.config;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.context.annotation.Import;
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupportUnitTests.DummyRegistrar;
import org.springframework.data.repository.core.support.DummyRepositoryFactoryBean;
@Retention(RetentionPolicy.RUNTIME)
@Import(DummyRegistrar.class)
@Inherited
public @interface EnableRepositories {
String[] value() default {};
@@ -34,7 +39,7 @@ public @interface EnableRepositories {
Filter[] excludeFilters() default {};
Class<?> repositoryFactoryBeanClass() default RepositoryFactoryBeanSupport.class;
Class<?> repositoryFactoryBeanClass() default DummyRepositoryFactoryBean.class;
String namedQueriesLocation() default "";

View File

@@ -15,70 +15,35 @@
*/
package org.springframework.data.repository.config;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.lang.annotation.Annotation;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
/**
* Integration test for {@link RepositoryBeanDefinitionRegistrarSupport}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class RepositoryBeanDefinitionRegistrarSupportIntegrationTests {
@Mock
BeanDefinitionRegistry registry;
@Configuration
@EnableRepositories
static class SampleConfig {
}
@Configuration
static class TestConfig extends SampleConfig {
}
@Test
public void registersBeanDefinitionForFoundBean() {
public void testBootstrappingWithInheritedConfigClasses() {
AnnotationMetadata metadata = new StandardAnnotationMetadata(SampleConfiguration.class, true);
DummyRegistrar registrar = new DummyRegistrar();
registrar.registerBeanDefinitions(metadata, registry);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
verify(registry, times(1)).registerBeanDefinition(eq("myRepository"), any(BeanDefinition.class));
}
private static class DummyRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableRepositories.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension()
*/
@Override
protected RepositoryConfigurationExtension getExtension() {
return new RepositoryConfigurationExtensionSupport() {
public String getRepositoryFactoryClassName() {
return RepositoryFactoryBeanSupport.class.getName();
}
@Override
protected String getModulePrefix() {
return "commons";
}
};
}
assertThat(context.getBean(MyRepository.class), is(notNullValue()));
assertThat(context.getBean(MyOtherRepository.class), is(notNullValue()));
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2012 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.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.lang.annotation.Annotation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
/**
* Integration test for {@link RepositoryBeanDefinitionRegistrarSupport}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
@Mock
BeanDefinitionRegistry registry;
@Test
public void registersBeanDefinitionForFoundBean() {
AnnotationMetadata metadata = new StandardAnnotationMetadata(SampleConfiguration.class, true);
DummyRegistrar registrar = new DummyRegistrar();
registrar.registerBeanDefinitions(metadata, registry);
verify(registry, times(1)).registerBeanDefinition(eq("myRepository"), any(BeanDefinition.class));
}
static class DummyRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableRepositories.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension()
*/
@Override
protected RepositoryConfigurationExtension getExtension() {
return new RepositoryConfigurationExtensionSupport() {
public String getRepositoryFactoryClassName() {
return RepositoryFactoryBeanSupport.class.getName();
}
@Override
protected String getModulePrefix() {
return "commons";
}
};
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2012 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.core.support;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.mockito.Mockito;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupportUnitTests.MyRepositoryQuery;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
public class DummyRepositoryFactory extends RepositoryFactorySupport {
private final Object repository;
public DummyRepositoryFactory(Object repository) {
this.repository = repository;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
return mock(EntityInformation.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata)
*/
@Override
protected Object getTargetRepository(RepositoryMetadata metadata) {
return repository;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata)
*/
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return repository.getClass();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key)
*/
@Override
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
MyRepositoryQuery queryOne = mock(MyRepositoryQuery.class);
RepositoryQuery queryTwo = mock(RepositoryQuery.class);
QueryLookupStrategy strategy = mock(QueryLookupStrategy.class);
when(
strategy.resolveQuery(Mockito.any(Method.class), Mockito.any(RepositoryMetadata.class),
Mockito.any(NamedQueries.class))).thenReturn(queryOne, queryTwo);
return strategy;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2012 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.core.support;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import org.springframework.data.repository.Repository;
/**
* @author Oliver Gierke
*/
public class DummyRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends
RepositoryFactoryBeanSupport<T, S, ID> {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory()
*/
@Override
protected RepositoryFactorySupport createRepositoryFactory() {
Repository<?, ?> repository = mock(Repository.class);
return new DummyRepositoryFactory(repository);
}
}

View File

@@ -20,9 +20,9 @@ import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -35,11 +35,6 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.RepositoryDefinition;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
/**
@@ -50,7 +45,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
@RunWith(MockitoJUnitRunner.class)
public class RepositoryFactorySupportUnitTests {
RepositoryFactorySupport factory = new DummyRepositoryFactory();
RepositoryFactorySupport factory;
@Mock
PagingAndSortingRepository<Object, Serializable> backingRepo;
@@ -62,6 +57,11 @@ public class RepositoryFactorySupportUnitTests {
@Mock
PlainQueryCreationListener otherListener;
@Before
public void setUp() {
factory = new DummyRepositoryFactory(backingRepo);
}
@Test
public void invokesCustomQueryCreationListenerForSpecialRepositoryQueryOnly() throws Exception {
@@ -113,45 +113,6 @@ public class RepositoryFactorySupportUnitTests {
assertThat(factory.getRepository(foo), is(notNullValue()));
}
class DummyRepositoryFactory extends RepositoryFactorySupport {
/* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
return mock(EntityInformation.class);
}
@Override
protected Object getTargetRepository(RepositoryMetadata metadata) {
return backingRepo;
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return backingRepo.getClass();
}
@Override
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
MyRepositoryQuery queryOne = mock(MyRepositoryQuery.class);
RepositoryQuery queryTwo = mock(RepositoryQuery.class);
QueryLookupStrategy strategy = mock(QueryLookupStrategy.class);
when(
strategy.resolveQuery(Mockito.any(Method.class), Mockito.any(RepositoryMetadata.class),
Mockito.any(NamedQueries.class))).thenReturn(queryOne, queryTwo);
return strategy;
}
}
interface ObjectRepository extends Repository<Object, Serializable>, ObjectRepositoryCustom {
Object findByClass(Class<?> clazz);