Skip to content

Commit 874da36

Browse files
committed
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.
1 parent 075e31d commit 874da36

File tree

6 files changed

+186
-6
lines changed

6 files changed

+186
-6
lines changed

spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,17 @@ private void registerGenericRepositoryFactoryBean(
104104
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
105105
beanDefinition.setSource(configuration.getSource());
106106

107+
RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator();
108+
generator.setBeanClassLoader(parser.getReaderContext().getBeanClassLoader());
109+
110+
String beanName = generator.generateBeanName(beanDefinition, parser.getRegistry());
111+
107112
if (LOG.isDebugEnabled()) {
108-
LOG.debug("Registering repository: " + configuration.getBeanId() + " - Interface: "
109-
+ configuration.getRepositoryInterface() + " - Factory: " + extension.getRepositoryFactoryClassName());
113+
LOG.debug("Registering repository: " + beanName + " - Interface: " + configuration.getRepositoryInterface()
114+
+ " - Factory: " + extension.getRepositoryFactoryClassName());
110115
}
111116

112-
BeanComponentDefinition definition = new BeanComponentDefinition(beanDefinition, configuration.getBeanId());
117+
BeanComponentDefinition definition = new BeanComponentDefinition(beanDefinition, beanName);
113118
parser.registerBeanComponent(definition);
114119
} catch (RuntimeException e) {
115120
handleError(e, configuration.getConfigurationSource().getElement(), parser.getReaderContext());

spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.lang.annotation.Annotation;
1919

2020
import org.springframework.beans.factory.config.BeanDefinition;
21+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2122
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2223
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2324
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
@@ -33,6 +34,9 @@
3334
*/
3435
public abstract class RepositoryBeanDefinitionRegistrarSupport implements ImportBeanDefinitionRegistrar {
3536

37+
// see SPR-9568
38+
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
39+
3640
/*
3741
* (non-Javadoc)
3842
* @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
4751
return;
4852
}
4953

50-
ResourceLoader resourceLoader = new DefaultResourceLoader();
5154
AnnotationRepositoryConfigurationSource configuration = new AnnotationRepositoryConfigurationSource(
5255
annotationMetadata, getAnnotation());
5356

5457
RepositoryConfigurationExtension extension = getExtension();
5558
extension.registerBeansForRoot(registry, configuration);
5659

60+
RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator();
61+
generator.setBeanClassLoader(getBeanClassLoader(registry));
62+
5763
for (RepositoryConfiguration<AnnotationRepositoryConfigurationSource> repositoryConfiguration : extension
5864
.getRepositoryConfigurations(configuration, resourceLoader)) {
5965

@@ -62,10 +68,20 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD
6268

6369
extension.postProcess(definitionBuilder, configuration);
6470

65-
registry.registerBeanDefinition(repositoryConfiguration.getBeanId(), definitionBuilder.getBeanDefinition());
71+
String beanName = generator.generateBeanName(definitionBuilder.getBeanDefinition(), registry);
72+
registry.registerBeanDefinition(beanName, definitionBuilder.getBeanDefinition());
6673
}
6774
}
6875

76+
private ClassLoader getBeanClassLoader(BeanDefinitionRegistry registry) {
77+
78+
if (registry instanceof ConfigurableListableBeanFactory) {
79+
return ((ConfigurableListableBeanFactory) registry).getBeanClassLoader();
80+
}
81+
82+
return resourceLoader.getClassLoader();
83+
}
84+
6985
/**
7086
* Return the annotation to obtain configuration information from. Will be wrappen into an
7187
* {@link AnnotationRepositoryConfigurationSource} so have a look at the constants in there for what annotation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.config;
17+
18+
import org.springframework.beans.factory.BeanClassLoaderAware;
19+
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
20+
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
21+
import org.springframework.beans.factory.config.BeanDefinition;
22+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
23+
import org.springframework.beans.factory.support.BeanNameGenerator;
24+
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
25+
import org.springframework.util.ClassUtils;
26+
27+
/**
28+
* Special {@link BeanNameGenerator} to create bean names for Spring Data repositories. Will delegate to an
29+
* {@link AnnotationBeanNameGenerator} but let the delegate work with a customized {@link BeanDefinition} to make sure
30+
* the repository interface is inspected and not the actual bean definition class.
31+
*
32+
* @author Oliver Gierke
33+
*/
34+
public class RepositoryBeanNameGenerator implements BeanNameGenerator, BeanClassLoaderAware {
35+
36+
private static final BeanNameGenerator DELEGATE = new AnnotationBeanNameGenerator();
37+
38+
private ClassLoader beanClassLoader;
39+
40+
/*
41+
* (non-Javadoc)
42+
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
43+
*/
44+
public void setBeanClassLoader(ClassLoader classLoader) {
45+
this.beanClassLoader = classLoader;
46+
}
47+
48+
/*
49+
* (non-Javadoc)
50+
* @see org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
51+
*/
52+
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
53+
54+
AnnotatedBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(getRepositoryInterfaceFrom(definition));
55+
return DELEGATE.generateBeanName(beanDefinition, registry);
56+
}
57+
58+
/**
59+
* Returns the type configured for the {@code repositoryInterface} property of the given bean definition. Uses a
60+
* potential {@link Class} being configured as is or tries to load a class with the given value's {@link #toString()}
61+
* representation.
62+
*
63+
* @param beanDefinition
64+
* @return
65+
*/
66+
private Class<?> getRepositoryInterfaceFrom(BeanDefinition beanDefinition) {
67+
68+
Object value = beanDefinition.getPropertyValues().getPropertyValue("repositoryInterface").getValue();
69+
70+
if (value instanceof Class<?>) {
71+
return (Class<?>) value;
72+
} else {
73+
try {
74+
return ClassUtils.forName(value.toString(), beanClassLoader);
75+
} catch (Exception o_O) {
76+
throw new RuntimeException(o_O);
77+
}
78+
}
79+
}
80+
}

spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.springframework.beans.factory.FactoryBean;
1919
import org.springframework.beans.factory.config.BeanDefinition;
20+
import org.springframework.beans.factory.support.BeanNameGenerator;
2021
import org.springframework.data.repository.query.QueryLookupStrategy;
2122

2223
/**
@@ -30,7 +31,9 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
3031
* Returns the id of the {@link BeanDefinition} the repository shall be registered under.
3132
*
3233
* @return
34+
* @deprecated bean ids should be determined using a {@link BeanNameGenerator} during classpath scanning.
3335
*/
36+
@Deprecated
3437
String getBeanId();
3538

3639
/**

spring-data-commons-core/src/test/java/org/springframework/data/repository/config/DefaultRepositoryConfigurationUnitTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ public void supportsBasicConfiguration() {
4141
RepositoryConfiguration<RepositoryConfigurationSource> configuration = new DefaultRepositoryConfiguration<RepositoryConfigurationSource>(
4242
source, "com.acme.MyRepository");
4343

44-
assertThat(configuration.getBeanId(), is("myRepository"));
4544
assertThat(configuration.getConfigurationSource(), is(source));
4645
assertThat(configuration.getImplementationBeanName(), is("myRepositoryImpl"));
4746
assertThat(configuration.getImplementationClassName(), is("MyRepositoryImpl"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.config;
17+
18+
import static org.hamcrest.CoreMatchers.*;
19+
import static org.junit.Assert.*;
20+
21+
import javax.inject.Named;
22+
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
import org.springframework.beans.factory.config.BeanDefinition;
26+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
27+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
28+
import org.springframework.beans.factory.support.BeanNameGenerator;
29+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
30+
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
31+
32+
/**
33+
* Unit tests for {@link RepositoryBeanNameGenerator}.
34+
*
35+
* @author Oliver Gierke
36+
*/
37+
public class RepositoryBeanNameGeneratorUnitTest {
38+
39+
BeanNameGenerator generator;
40+
BeanDefinitionRegistry registry;
41+
42+
@Before
43+
public void setUp() {
44+
45+
RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator();
46+
generator.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
47+
48+
this.generator = generator;
49+
this.registry = new DefaultListableBeanFactory();
50+
}
51+
52+
@Test
53+
public void usesPlainClassNameIfNoAnnotationPresent() {
54+
assertThat(generator.generateBeanName(getBeanDefinitionFor(MyRepository.class), registry), is("myRepository"));
55+
}
56+
57+
@Test
58+
public void usesAnnotationValueIfAnnotationPresent() {
59+
assertThat(generator.generateBeanName(getBeanDefinitionFor(AnnotatedInterface.class), registry), is("specialName"));
60+
}
61+
62+
private BeanDefinition getBeanDefinitionFor(Class<?> repositoryInterface) {
63+
64+
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RepositoryFactoryBeanSupport.class);
65+
builder.addPropertyValue("repositoryInterface", repositoryInterface.getName());
66+
return builder.getBeanDefinition();
67+
}
68+
69+
interface PlainInterface {
70+
71+
}
72+
73+
@Named("specialName")
74+
interface AnnotatedInterface {
75+
76+
}
77+
}

0 commit comments

Comments
 (0)