diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java index 4e5435b09..ccb55f049 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java @@ -104,12 +104,17 @@ public class RepositoryBeanDefinitionParser implements BeanDefinitionParser { AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); beanDefinition.setSource(configuration.getSource()); + RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator(); + generator.setBeanClassLoader(parser.getReaderContext().getBeanClassLoader()); + + String beanName = generator.generateBeanName(beanDefinition, parser.getRegistry()); + if (LOG.isDebugEnabled()) { - LOG.debug("Registering repository: " + configuration.getBeanId() + " - Interface: " - + configuration.getRepositoryInterface() + " - Factory: " + extension.getRepositoryFactoryClassName()); + LOG.debug("Registering repository: " + beanName + " - Interface: " + configuration.getRepositoryInterface() + + " - Factory: " + extension.getRepositoryFactoryClassName()); } - BeanComponentDefinition definition = new BeanComponentDefinition(beanDefinition, configuration.getBeanId()); + BeanComponentDefinition definition = new BeanComponentDefinition(beanDefinition, beanName); parser.registerBeanComponent(definition); } catch (RuntimeException e) { handleError(e, configuration.getConfigurationSource().getElement(), parser.getReaderContext()); diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java index 82a102ac9..b676d2811 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java @@ -18,6 +18,7 @@ package org.springframework.data.repository.config; import java.lang.annotation.Annotation; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; @@ -33,6 +34,9 @@ import org.springframework.util.Assert; */ public abstract class RepositoryBeanDefinitionRegistrarSupport implements ImportBeanDefinitionRegistrar { + // see SPR-9568 + private final ResourceLoader resourceLoader = new DefaultResourceLoader(); + /* * (non-Javadoc) * @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry) @@ -47,13 +51,15 @@ public abstract class RepositoryBeanDefinitionRegistrarSupport implements Import return; } - ResourceLoader resourceLoader = new DefaultResourceLoader(); AnnotationRepositoryConfigurationSource configuration = new AnnotationRepositoryConfigurationSource( annotationMetadata, getAnnotation()); RepositoryConfigurationExtension extension = getExtension(); extension.registerBeansForRoot(registry, configuration); + RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator(); + generator.setBeanClassLoader(getBeanClassLoader(registry)); + for (RepositoryConfiguration repositoryConfiguration : extension .getRepositoryConfigurations(configuration, resourceLoader)) { @@ -62,10 +68,20 @@ public abstract class RepositoryBeanDefinitionRegistrarSupport implements Import extension.postProcess(definitionBuilder, configuration); - registry.registerBeanDefinition(repositoryConfiguration.getBeanId(), definitionBuilder.getBeanDefinition()); + String beanName = generator.generateBeanName(definitionBuilder.getBeanDefinition(), registry); + registry.registerBeanDefinition(beanName, definitionBuilder.getBeanDefinition()); } } + private ClassLoader getBeanClassLoader(BeanDefinitionRegistry registry) { + + if (registry instanceof ConfigurableListableBeanFactory) { + return ((ConfigurableListableBeanFactory) registry).getBeanClassLoader(); + } + + return resourceLoader.getClassLoader(); + } + /** * Return the annotation to obtain configuration information from. Will be wrappen into an * {@link AnnotationRepositoryConfigurationSource} so have a look at the constants in there for what annotation diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanNameGenerator.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanNameGenerator.java new file mode 100644 index 000000000..667adcc20 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanNameGenerator.java @@ -0,0 +1,80 @@ +/* + * 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 org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.context.annotation.AnnotationBeanNameGenerator; +import org.springframework.util.ClassUtils; + +/** + * Special {@link BeanNameGenerator} to create bean names for Spring Data repositories. Will delegate to an + * {@link AnnotationBeanNameGenerator} but let the delegate work with a customized {@link BeanDefinition} to make sure + * the repository interface is inspected and not the actual bean definition class. + * + * @author Oliver Gierke + */ +public class RepositoryBeanNameGenerator implements BeanNameGenerator, BeanClassLoaderAware { + + private static final BeanNameGenerator DELEGATE = new AnnotationBeanNameGenerator(); + + private ClassLoader beanClassLoader; + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader) + */ + public void setBeanClassLoader(ClassLoader classLoader) { + this.beanClassLoader = classLoader; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry) + */ + public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { + + AnnotatedBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(getRepositoryInterfaceFrom(definition)); + return DELEGATE.generateBeanName(beanDefinition, registry); + } + + /** + * Returns the type configured for the {@code repositoryInterface} property of the given bean definition. Uses a + * potential {@link Class} being configured as is or tries to load a class with the given value's {@link #toString()} + * representation. + * + * @param beanDefinition + * @return + */ + private Class getRepositoryInterfaceFrom(BeanDefinition beanDefinition) { + + Object value = beanDefinition.getPropertyValues().getPropertyValue("repositoryInterface").getValue(); + + if (value instanceof Class) { + return (Class) value; + } else { + try { + return ClassUtils.forName(value.toString(), beanClassLoader); + } catch (Exception o_O) { + throw new RuntimeException(o_O); + } + } + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java index d557201c8..e778ca517 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.data.repository.config; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.data.repository.query.QueryLookupStrategy; /** @@ -30,7 +31,9 @@ public interface RepositoryConfiguration configuration = new DefaultRepositoryConfiguration( source, "com.acme.MyRepository"); - assertThat(configuration.getBeanId(), is("myRepository")); assertThat(configuration.getConfigurationSource(), is(source)); assertThat(configuration.getImplementationBeanName(), is("myRepositoryImpl")); assertThat(configuration.getImplementationClassName(), is("MyRepositoryImpl")); diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/config/RepositoryBeanNameGeneratorUnitTest.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/config/RepositoryBeanNameGeneratorUnitTest.java new file mode 100644 index 000000000..52fa792a0 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/config/RepositoryBeanNameGeneratorUnitTest.java @@ -0,0 +1,77 @@ +/* + * 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.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import javax.inject.Named; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; + +/** + * Unit tests for {@link RepositoryBeanNameGenerator}. + * + * @author Oliver Gierke + */ +public class RepositoryBeanNameGeneratorUnitTest { + + BeanNameGenerator generator; + BeanDefinitionRegistry registry; + + @Before + public void setUp() { + + RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator(); + generator.setBeanClassLoader(Thread.currentThread().getContextClassLoader()); + + this.generator = generator; + this.registry = new DefaultListableBeanFactory(); + } + + @Test + public void usesPlainClassNameIfNoAnnotationPresent() { + assertThat(generator.generateBeanName(getBeanDefinitionFor(MyRepository.class), registry), is("myRepository")); + } + + @Test + public void usesAnnotationValueIfAnnotationPresent() { + assertThat(generator.generateBeanName(getBeanDefinitionFor(AnnotatedInterface.class), registry), is("specialName")); + } + + private BeanDefinition getBeanDefinitionFor(Class repositoryInterface) { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RepositoryFactoryBeanSupport.class); + builder.addPropertyValue("repositoryInterface", repositoryInterface.getName()); + return builder.getBeanDefinition(); + } + + interface PlainInterface { + + } + + @Named("specialName") + interface AnnotatedInterface { + + } +}