Skip to content

Commit 70cda79

Browse files
mp911dechristophstrobl
authored andcommitted
Consider component names for custom implementation and fragment bean registration.
We now consider the the actual repository & fragment bean name when checking for existing bean definitions of default custom implementation beans. Previously, we used the repository interface name without considering the repository bean name. Closes #2487. Original Pull Request: #2488
1 parent 103d41f commit 70cda79

18 files changed

+444
-88
lines changed

src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Collection;
1919
import java.util.Optional;
2020
import java.util.Set;
21+
import java.util.function.Supplier;
2122
import java.util.stream.Collectors;
2223

2324
import org.springframework.beans.factory.config.BeanDefinition;
@@ -107,8 +108,22 @@ public Optional<AbstractBeanDefinition> detectCustomImplementation(Implementatio
107108
.filter(lookup::matches) //
108109
.collect(StreamUtils.toUnmodifiableSet());
109110

111+
return selectImplementationCandidate(lookup, definitions, () -> {
112+
113+
if (definitions.isEmpty()) {
114+
return Optional.empty();
115+
}
116+
117+
return Optional.of(definitions.iterator().next());
118+
});
119+
}
120+
121+
private static Optional<AbstractBeanDefinition> selectImplementationCandidate(
122+
ImplementationLookupConfiguration lookup, Set<BeanDefinition> definitions,
123+
Supplier<Optional<BeanDefinition>> fallback) {
124+
110125
return SelectionSet //
111-
.of(definitions, c -> c.isEmpty() ? Optional.empty() : throwAmbiguousCustomImplementationException(c)) //
126+
.of(definitions, c -> c.isEmpty() ? fallback.get() : throwAmbiguousCustomImplementationException(c)) //
112127
.filterIfNecessary(lookup::hasMatchingBeanName) //
113128
.uniqueResult() //
114129
.map(AbstractBeanDefinition.class::cast);

src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package org.springframework.data.repository.config;
1717

18-
import java.beans.Introspector;
1918
import java.io.IOException;
2019

2120
import org.springframework.beans.factory.config.BeanDefinition;
@@ -42,22 +41,16 @@ class DefaultImplementationLookupConfiguration implements ImplementationLookupCo
4241
private final String interfaceName;
4342
private final String beanName;
4443

45-
/**
46-
* Creates a new {@link DefaultImplementationLookupConfiguration} for the given
47-
* {@link ImplementationDetectionConfiguration} and interface name.
48-
*
49-
* @param config must not be {@literal null}.
50-
* @param interfaceName must not be {@literal null} or empty.
51-
*/
52-
DefaultImplementationLookupConfiguration(ImplementationDetectionConfiguration config, String interfaceName) {
44+
DefaultImplementationLookupConfiguration(ImplementationDetectionConfiguration config, String interfaceName,
45+
String beanName) {
5346

5447
Assert.notNull(config, "ImplementationDetectionConfiguration must not be null");
5548
Assert.hasText(interfaceName, "Interface name must not be null or empty");
49+
Assert.hasText(beanName, "Bean name must not be null or empty!");
5650

5751
this.config = config;
5852
this.interfaceName = interfaceName;
59-
this.beanName = Introspector
60-
.decapitalize(ClassUtils.getShortName(interfaceName).concat(config.getImplementationPostfix()));
53+
this.beanName = beanName;
6154
}
6255

6356
@Override

src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.core.type.filter.TypeFilter;
2323
import org.springframework.data.config.ConfigurationUtils;
2424
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
25+
import org.springframework.data.util.Lazy;
2526
import org.springframework.data.util.Streamable;
2627
import org.springframework.lang.Nullable;
2728
import org.springframework.util.Assert;
@@ -45,13 +46,15 @@ public class DefaultRepositoryConfiguration<T extends RepositoryConfigurationSou
4546
private final T configurationSource;
4647
private final BeanDefinition definition;
4748
private final RepositoryConfigurationExtension extension;
49+
private final Lazy<String> beanName;
4850

4951
public DefaultRepositoryConfiguration(T configurationSource, BeanDefinition definition,
5052
RepositoryConfigurationExtension extension) {
5153

5254
this.configurationSource = configurationSource;
5355
this.definition = definition;
5456
this.extension = extension;
57+
this.beanName = Lazy.of(() -> configurationSource.generateBeanName(definition));
5558
}
5659

5760
public String getBeanId() {
@@ -90,8 +93,7 @@ public String getImplementationClassName() {
9093
}
9194

9295
public String getImplementationBeanName() {
93-
return configurationSource.generateBeanName(definition)
94-
+ configurationSource.getRepositoryImplementationPostfix().orElse("Impl");
96+
return beanName.get() + configurationSource.getRepositoryImplementationPostfix().orElse("Impl");
9597
}
9698

9799
@Nullable
@@ -117,6 +119,11 @@ public String getRepositoryFactoryBeanClassName() {
117119
.orElseGet(extension::getRepositoryFactoryBeanClassName);
118120
}
119121

122+
@Override
123+
public String getRepositoryBeanName() {
124+
return beanName.get();
125+
}
126+
120127
@Override
121128
public boolean isLazyInit() {
122129
return definition.isLazyInit() || !configurationSource.getBootstrapMode().equals(BootstrapMode.DEFAULT);

src/main/java/org/springframework/data/repository/config/ImplementationDetectionConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ default ImplementationLookupConfiguration forFragment(String fragmentInterfaceNa
9090

9191
Assert.hasText(fragmentInterfaceName, "Fragment interface name must not be null or empty");
9292

93-
return new DefaultImplementationLookupConfiguration(this, fragmentInterfaceName);
93+
return new DefaultImplementationLookupConfiguration(this, fragmentInterfaceName,
94+
Introspector.decapitalize(ClassUtils.getShortName(fragmentInterfaceName).concat(getImplementationPostfix())));
9495
}
9596

9697
/**
@@ -103,7 +104,8 @@ default ImplementationLookupConfiguration forRepositoryConfiguration(RepositoryC
103104

104105
Assert.notNull(config, "RepositoryConfiguration must not be null");
105106

106-
return new DefaultImplementationLookupConfiguration(this, config.getRepositoryInterface()) {
107+
return new DefaultImplementationLookupConfiguration(this, config.getRepositoryInterface(),
108+
config.getImplementationBeanName()) {
107109

108110
@Override
109111
public Streamable<String> getBasePackages() {

src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ RepositoryConfigurationAdapter<?> buildMetadata(RepositoryConfiguration<?> confi
147147

148148
List<RepositoryFragmentConfiguration> repositoryFragmentConfigurationStream = fragmentMetadata
149149
.getFragmentInterfaces(configuration.getRepositoryInterface()) //
150-
.map(it -> detectRepositoryFragmentConfiguration(it, config)) //
150+
.map(it -> detectRepositoryFragmentConfiguration(it, config, configuration)) //
151151
.flatMap(Optionals::toStream).toList();
152152

153153
if (repositoryFragmentConfigurationStream.isEmpty()) {
@@ -183,26 +183,42 @@ private Optional<String> registerCustomImplementation(RepositoryConfiguration<?>
183183

184184
ImplementationLookupConfiguration lookup = configuration.toLookupConfiguration(metadataReaderFactory);
185185

186-
String beanName = lookup.getImplementationBeanName();
186+
String configurationBeanName = lookup.getImplementationBeanName();
187187

188188
// Already a bean configured?
189-
if (registry.containsBeanDefinition(beanName)) {
190-
return Optional.of(beanName);
189+
if (registry.containsBeanDefinition(configurationBeanName)) {
190+
191+
if (logger.isDebugEnabled()) {
192+
logger.debug(String.format("Custom repository implementation already registered: %s", configurationBeanName));
193+
}
194+
195+
return Optional.of(configurationBeanName);
191196
}
192197

193198
Optional<AbstractBeanDefinition> beanDefinition = implementationDetector.detectCustomImplementation(lookup);
194199

195200
return beanDefinition.map(it -> {
196201

197-
if (logger.isDebugEnabled()) {
198-
logger.debug("Registering custom repository implementation: " + lookup.getImplementationBeanName() + " "
199-
+ it.getBeanClassName());
200-
}
201-
202+
String scannedBeanName = configuration.getConfigurationSource().generateBeanName(it);
202203
it.setSource(configuration.getSource());
203-
registry.registerBeanDefinition(beanName, it);
204204

205-
return beanName;
205+
if (registry.containsBeanDefinition(scannedBeanName)) {
206+
207+
if (logger.isDebugEnabled()) {
208+
logger.debug(String.format("Custom repository implementation already registered: %s %s", scannedBeanName,
209+
it.getBeanClassName()));
210+
}
211+
} else {
212+
213+
if (logger.isDebugEnabled()) {
214+
logger.debug(String.format("Registering custom repository implementation: %s %s", scannedBeanName,
215+
it.getBeanClassName()));
216+
}
217+
218+
registry.registerBeanDefinition(scannedBeanName, it);
219+
}
220+
221+
return scannedBeanName;
206222
});
207223
}
208224

@@ -232,19 +248,20 @@ private Stream<RepositoryFragmentConfiguration> registerRepositoryFragmentsImple
232248
.toImplementationDetectionConfiguration(metadataReaderFactory);
233249

234250
return fragmentMetadata.getFragmentInterfaces(configuration.getRepositoryInterface()) //
235-
.map(it -> detectRepositoryFragmentConfiguration(it, config)) //
251+
.map(it -> detectRepositoryFragmentConfiguration(it, config, configuration)) //
236252
.flatMap(Optionals::toStream) //
237253
.peek(it -> potentiallyRegisterFragmentImplementation(configuration, it)) //
238254
.peek(it -> potentiallyRegisterRepositoryFragment(configuration, it));
239255
}
240256

241257
private Optional<RepositoryFragmentConfiguration> detectRepositoryFragmentConfiguration(String fragmentInterface,
242-
ImplementationDetectionConfiguration config) {
258+
ImplementationDetectionConfiguration config, RepositoryConfiguration<?> configuration) {
243259

244260
ImplementationLookupConfiguration lookup = config.forFragment(fragmentInterface);
245261
Optional<AbstractBeanDefinition> beanDefinition = implementationDetector.detectCustomImplementation(lookup);
246262

247-
return beanDefinition.map(bd -> new RepositoryFragmentConfiguration(fragmentInterface, bd));
263+
return beanDefinition.map(bd -> new RepositoryFragmentConfiguration(fragmentInterface, bd,
264+
configuration.getConfigurationSource().generateBeanName(bd)));
248265
}
249266

250267
private void potentiallyRegisterFragmentImplementation(RepositoryConfiguration<?> repositoryConfiguration,
@@ -254,16 +271,21 @@ private void potentiallyRegisterFragmentImplementation(RepositoryConfiguration<?
254271

255272
// Already a bean configured?
256273
if (registry.containsBeanDefinition(beanName)) {
257-
return;
258-
}
259274

260-
if (logger.isDebugEnabled()) {
261-
logger.debug(String.format("Registering repository fragment implementation: %s %s", beanName,
262-
fragmentConfiguration.getClassName()));
275+
if (logger.isDebugEnabled()) {
276+
logger.debug(String.format("Repository fragment implementation already registered: %s", beanName));
277+
}
278+
279+
return;
263280
}
264281

265282
fragmentConfiguration.getBeanDefinition().ifPresent(bd -> {
266283

284+
if (logger.isDebugEnabled()) {
285+
logger.debug(String.format("Registering repository fragment implementation: %s %s", beanName,
286+
fragmentConfiguration.getClassName()));
287+
}
288+
267289
bd.setSource(repositoryConfiguration.getSource());
268290
registry.registerBeanDefinition(beanName, bd);
269291
});
@@ -276,11 +298,16 @@ private void potentiallyRegisterRepositoryFragment(RepositoryConfiguration<?> co
276298

277299
// Already a bean configured?
278300
if (registry.containsBeanDefinition(beanName)) {
301+
302+
if (logger.isDebugEnabled()) {
303+
logger.debug(String.format("RepositoryFragment already registered: %s", beanName));
304+
}
305+
279306
return;
280307
}
281308

282309
if (logger.isDebugEnabled()) {
283-
logger.debug("Registering repository fragment: " + beanName);
310+
logger.debug(String.format("Registering RepositoryFragment: %s", beanName));
284311
}
285312

286313
BeanDefinitionBuilder fragmentBuilder = BeanDefinitionBuilder.rootBeanDefinition(RepositoryFragment.class,

src/main/java/org/springframework/data/repository/config/RepositoryConfiguration.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,22 @@ public interface RepositoryConfiguration<T extends RepositoryConfigurationSource
8484
*/
8585
String getRepositoryFactoryBeanClassName();
8686

87+
/**
88+
* Returns the custom implementation bean name to be used.
89+
*
90+
* @return
91+
* @since 3.0
92+
*/
93+
String getImplementationBeanName();
94+
95+
/**
96+
* Returns the bean name of the repository to be used.
97+
*
98+
* @return
99+
* @since 3.0
100+
*/
101+
String getRepositoryBeanName();
102+
87103
/**
88104
* Returns the source of the {@link RepositoryConfiguration}.
89105
*

src/main/java/org/springframework/data/repository/config/RepositoryConfigurationAdapter.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ public String getRepositoryFactoryBeanClassName() {
7575
return repositoryConfiguration.getRepositoryFactoryBeanClassName();
7676
}
7777

78+
@Override
79+
public String getImplementationBeanName() {
80+
return repositoryConfiguration.getImplementationBeanName();
81+
}
82+
83+
@Override
84+
public String getRepositoryBeanName() {
85+
return repositoryConfiguration.getRepositoryBeanName();
86+
}
87+
7888
@Override
7989
@Nullable
8090
public Object getSource() {

src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfiguration.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public final class RepositoryFragmentConfiguration {
3636
private final String interfaceName;
3737
private final String className;
3838
private final Optional<AbstractBeanDefinition> beanDefinition;
39+
private final String beanName;
3940

4041
/**
4142
* Creates a {@link RepositoryFragmentConfiguration} given {@code interfaceName} and {@code className} of the
@@ -45,13 +46,7 @@ public final class RepositoryFragmentConfiguration {
4546
* @param className must not be {@literal null} or empty.
4647
*/
4748
public RepositoryFragmentConfiguration(String interfaceName, String className) {
48-
49-
Assert.hasText(interfaceName, "Interface name must not be null or empty");
50-
Assert.hasText(className, "Class name must not be null or empty");
51-
52-
this.interfaceName = interfaceName;
53-
this.className = className;
54-
this.beanDefinition = Optional.empty();
49+
this(interfaceName, className, Optional.empty(), generateBeanName(className));
5550
}
5651

5752
/**
@@ -69,20 +64,40 @@ public RepositoryFragmentConfiguration(String interfaceName, AbstractBeanDefinit
6964
this.interfaceName = interfaceName;
7065
this.className = ConfigurationUtils.getRequiredBeanClassName(beanDefinition);
7166
this.beanDefinition = Optional.of(beanDefinition);
67+
this.beanName = generateBeanName();
68+
}
69+
70+
RepositoryFragmentConfiguration(String interfaceName, AbstractBeanDefinition beanDefinition, String beanName) {
71+
this(interfaceName, ConfigurationUtils.getRequiredBeanClassName(beanDefinition), Optional.of(beanDefinition),
72+
beanName);
7273
}
7374

74-
public RepositoryFragmentConfiguration(String interfaceName, String className,
75-
Optional<AbstractBeanDefinition> beanDefinition) {
75+
private RepositoryFragmentConfiguration(String interfaceName, String className,
76+
Optional<AbstractBeanDefinition> beanDefinition, String beanName) {
77+
78+
Assert.hasText(interfaceName, "Interface name must not be null or empty!");
79+
Assert.notNull(beanDefinition, "Bean definition must not be null!");
80+
Assert.notNull(beanName, "Bean name must not be null!");
81+
7682
this.interfaceName = interfaceName;
7783
this.className = className;
7884
this.beanDefinition = beanDefinition;
85+
this.beanName = beanName;
86+
}
87+
88+
private String generateBeanName() {
89+
return generateBeanName(getClassName());
90+
}
91+
92+
private static String generateBeanName(String className) {
93+
return Introspector.decapitalize(ClassUtils.getShortName(className));
7994
}
8095

8196
/**
8297
* @return name of the implementation bean.
8398
*/
8499
public String getImplementationBeanName() {
85-
return Introspector.decapitalize(ClassUtils.getShortName(getClassName()));
100+
return this.beanName;
86101
}
87102

88103
/**

0 commit comments

Comments
 (0)