Skip to content

Commit

Permalink
DATACMNS-168 - Allow customizing repository bean names.
Browse files Browse the repository at this point in the history
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
odrotbohm committed Nov 12, 2012
1 parent 8007e25 commit b4a553e
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 6 deletions.
Expand Up @@ -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());
Expand Down
Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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)) {

Expand All @@ -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
Expand Down
@@ -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);
}
}
}
}
Expand Up @@ -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;

/**
Expand All @@ -30,7 +31,9 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
* 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();

/**
Expand Down
Expand Up @@ -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"));
Expand Down
@@ -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.