Permalink
Browse files

DATACMNS-168 - Allow customizing repository bean names.

The bean name is now resolved through inspecting Spring stereotype annotations and @Named on the repository interface. If none found we're still using the uncapitalized simple interface name as we did until now.
  • Loading branch information...
1 parent 8007e25 commit b4a553e2d83f34bf72287296663a975e5ba44419 @olivergierke olivergierke committed Nov 12, 2012
@@ -104,12 +104,17 @@ private void registerGenericRepositoryFactoryBean(
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());
@@ -18,6 +18,7 @@
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 @@
*/
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 void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD
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<AnnotationRepositoryConfigurationSource> repositoryConfiguration : extension
.getRepositoryConfigurations(configuration, resourceLoader)) {
@@ -62,10 +68,20 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD
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
@@ -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);
+ }
+ }
+ }
+}
@@ -17,6 +17,7 @@
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 @@
* Returns the id of the {@link BeanDefinition} the repository shall be registered under.
*
* @return
+ * @deprecated bean ids should be determined using a {@link BeanNameGenerator} during classpath scanning.
*/
+ @Deprecated
String getBeanId();
/**
@@ -41,7 +41,6 @@ public void supportsBasicConfiguration() {
RepositoryConfiguration<RepositoryConfigurationSource> configuration = new DefaultRepositoryConfiguration<RepositoryConfigurationSource>(
source, "com.acme.MyRepository");
- assertThat(configuration.getBeanId(), is("myRepository"));
assertThat(configuration.getConfigurationSource(), is(source));
assertThat(configuration.getImplementationBeanName(), is("myRepositoryImpl"));
assertThat(configuration.getImplementationClassName(), is("MyRepositoryImpl"));
@@ -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 {
+
+ }
+}

0 comments on commit b4a553e

Please sign in to comment.