19
19
20
20
import java .io .IOException ;
21
21
import java .util .ArrayList ;
22
+ import java .util .Collections ;
22
23
import java .util .List ;
23
24
import java .util .Locale ;
25
+ import java .util .Map ;
24
26
import java .util .Optional ;
25
27
import java .util .stream .Collectors ;
26
28
import java .util .stream .Stream ;
27
29
28
30
import org .apache .commons .logging .Log ;
29
31
import org .apache .commons .logging .LogFactory ;
32
+
33
+ import org .springframework .beans .factory .annotation .AnnotatedGenericBeanDefinition ;
30
34
import org .springframework .beans .factory .config .BeanDefinition ;
31
35
import org .springframework .beans .factory .config .RuntimeBeanReference ;
32
36
import org .springframework .beans .factory .support .AbstractBeanDefinition ;
35
39
import org .springframework .beans .factory .support .BeanDefinitionRegistry ;
36
40
import org .springframework .core .env .Environment ;
37
41
import org .springframework .core .io .ResourceLoader ;
42
+ import org .springframework .core .io .support .SpringFactoriesLoader ;
38
43
import org .springframework .core .type .classreading .CachingMetadataReaderFactory ;
44
+ import org .springframework .core .type .classreading .MetadataReader ;
39
45
import org .springframework .core .type .classreading .MetadataReaderFactory ;
40
46
import org .springframework .data .config .ParsingUtils ;
41
47
import org .springframework .data .repository .core .support .RepositoryFragment ;
42
48
import org .springframework .data .repository .core .support .RepositoryFragmentsFactoryBean ;
43
49
import org .springframework .data .util .Optionals ;
50
+ import org .springframework .lang .Nullable ;
44
51
import org .springframework .util .Assert ;
45
52
import org .springframework .util .ClassUtils ;
53
+ import org .springframework .util .StringUtils ;
46
54
47
55
/**
48
56
* Builder to create {@link BeanDefinitionBuilder} instance to eventually create Spring Data repository instances.
@@ -63,6 +71,7 @@ class RepositoryBeanDefinitionBuilder {
63
71
private final MetadataReaderFactory metadataReaderFactory ;
64
72
private final FragmentMetadata fragmentMetadata ;
65
73
private final CustomRepositoryImplementationDetector implementationDetector ;
74
+ private final RepositoryFactoriesLoader factoriesLoader ;
66
75
67
76
/**
68
77
* Creates a new {@link RepositoryBeanDefinitionBuilder} from the given {@link BeanDefinitionRegistry},
@@ -83,7 +92,7 @@ public RepositoryBeanDefinitionBuilder(BeanDefinitionRegistry registry, Reposito
83
92
this .registry = registry ;
84
93
this .extension = extension ;
85
94
this .resourceLoader = resourceLoader ;
86
-
95
+ this . factoriesLoader = RepositoryFactoriesLoader . forDefaultResourceLocation ( resourceLoader . getClassLoader ());
87
96
this .metadataReaderFactory = new CachingMetadataReaderFactory (resourceLoader );
88
97
89
98
this .fragmentMetadata = new FragmentMetadata (metadataReaderFactory );
@@ -139,6 +148,7 @@ public BeanDefinitionBuilder build(RepositoryConfiguration<?> configuration) {
139
148
}
140
149
141
150
// TODO: merge that with the one that creates the BD
151
+ // TODO: Add support for fragments discovered from spring.factories
142
152
RepositoryConfigurationAdapter <?> buildMetadata (RepositoryConfiguration <?> configuration ) {
143
153
144
154
ImplementationDetectionConfiguration config = configuration
@@ -223,21 +233,71 @@ private Stream<RepositoryFragmentConfiguration> registerRepositoryFragmentsImple
223
233
ImplementationDetectionConfiguration config = configuration
224
234
.toImplementationDetectionConfiguration (metadataReaderFactory );
225
235
226
- return fragmentMetadata .getFragmentInterfaces (configuration .getRepositoryInterface ()) //
227
- .map (it -> detectRepositoryFragmentConfiguration (it , config , configuration )) //
228
- .flatMap (Optionals ::toStream ) //
236
+ Stream <RepositoryFragmentConfiguration > discovered = discoverFragments (configuration , config );
237
+ Stream <RepositoryFragmentConfiguration > loaded = loadFragments (configuration );
238
+
239
+ return Stream .concat (discovered , loaded ) //
229
240
.peek (it -> potentiallyRegisterFragmentImplementation (configuration , it )) //
230
241
.peek (it -> potentiallyRegisterRepositoryFragment (configuration , it ));
231
242
}
232
243
244
+ private Stream <RepositoryFragmentConfiguration > discoverFragments (RepositoryConfiguration <?> configuration ,
245
+ ImplementationDetectionConfiguration config ) {
246
+ return fragmentMetadata .getFragmentInterfaces (configuration .getRepositoryInterface ())
247
+ .map (it -> detectRepositoryFragmentConfiguration (it , config , configuration )) //
248
+ .flatMap (Optionals ::toStream );
249
+ }
250
+
251
+ private Stream <RepositoryFragmentConfiguration > loadFragments (RepositoryConfiguration <?> configuration ) {
252
+
253
+ List <String > names = factoriesLoader .loadFactoryNames (configuration .getRepositoryInterface ());
254
+
255
+ if (names .isEmpty ()) {
256
+ return Stream .empty ();
257
+ }
258
+
259
+ return names .stream ().map (it -> createFragmentConfiguration (null , configuration , it ));
260
+ }
261
+
233
262
private Optional <RepositoryFragmentConfiguration > detectRepositoryFragmentConfiguration (String fragmentInterface ,
234
263
ImplementationDetectionConfiguration config , RepositoryConfiguration <?> configuration ) {
235
264
236
- ImplementationLookupConfiguration lookup = config .forFragment (fragmentInterface );
237
- Optional <AbstractBeanDefinition > beanDefinition = implementationDetector .detectCustomImplementation (lookup );
265
+ List <String > names = factoriesLoader .loadFactoryNames (fragmentInterface );
266
+
267
+ if (names .isEmpty ()) {
268
+
269
+ ImplementationLookupConfiguration lookup = config .forFragment (fragmentInterface );
270
+ Optional <AbstractBeanDefinition > beanDefinition = implementationDetector .detectCustomImplementation (lookup );
271
+
272
+ return beanDefinition .map (bd -> createFragmentConfiguration (fragmentInterface , configuration , bd ));
273
+ }
274
+
275
+ if (names .size () > 1 ) {
276
+ logger .debug (String .format ("Multiple fragment implementations %s registered for fragment interface %s" , names ,
277
+ fragmentInterface ));
278
+ }
279
+
280
+ return Optional .of (createFragmentConfiguration (fragmentInterface , configuration , names .get (0 )));
281
+ }
282
+
283
+ private RepositoryFragmentConfiguration createFragmentConfiguration (@ Nullable String fragmentInterface ,
284
+ RepositoryConfiguration <?> configuration , String className ) {
285
+
286
+ try {
238
287
239
- return beanDefinition .map (bd -> new RepositoryFragmentConfiguration (fragmentInterface , bd ,
240
- configuration .getConfigurationSource ().generateBeanName (bd )));
288
+ MetadataReader metadataReader = metadataReaderFactory .getMetadataReader (className );
289
+ AnnotatedGenericBeanDefinition bd = new AnnotatedGenericBeanDefinition (metadataReader .getAnnotationMetadata ());
290
+ return createFragmentConfiguration (fragmentInterface , configuration , bd );
291
+ } catch (IOException e ) {
292
+ throw new IllegalStateException (e );
293
+ }
294
+ }
295
+
296
+ private static RepositoryFragmentConfiguration createFragmentConfiguration (@ Nullable String fragmentInterface ,
297
+ RepositoryConfiguration <?> configuration , AbstractBeanDefinition beanDefinition ) {
298
+
299
+ return new RepositoryFragmentConfiguration (fragmentInterface , beanDefinition ,
300
+ configuration .getConfigurationSource ().generateBeanName (beanDefinition ));
241
301
}
242
302
243
303
private String potentiallyRegisterRepositoryImplementation (RepositoryConfiguration <?> configuration ,
@@ -314,10 +374,47 @@ private void potentiallyRegisterRepositoryFragment(RepositoryConfiguration<?> co
314
374
BeanDefinitionBuilder fragmentBuilder = BeanDefinitionBuilder .rootBeanDefinition (RepositoryFragment .class ,
315
375
"implemented" );
316
376
317
- fragmentBuilder .addConstructorArgValue (fragmentConfiguration .getInterfaceName ());
377
+ if (StringUtils .hasText (fragmentConfiguration .getInterfaceName ())) {
378
+ fragmentBuilder .addConstructorArgValue (fragmentConfiguration .getInterfaceName ());
379
+ }
318
380
fragmentBuilder .addConstructorArgReference (fragmentConfiguration .getImplementationBeanName ());
319
381
320
382
registry .registerBeanDefinition (beanName ,
321
383
ParsingUtils .getSourceBeanDefinition (fragmentBuilder , configuration .getSource ()));
322
384
}
385
+
386
+ static class RepositoryFactoriesLoader extends SpringFactoriesLoader {
387
+
388
+ private final Map <String , List <String >> factories ;
389
+
390
+ /**
391
+ * Create a new {@link SpringFactoriesLoader} instance.
392
+ *
393
+ * @param classLoader the classloader used to instantiate the factories
394
+ * @param factories a map of factory class name to implementation class names
395
+ */
396
+ protected RepositoryFactoriesLoader (@ Nullable ClassLoader classLoader , Map <String , List <String >> factories ) {
397
+ super (classLoader , factories );
398
+ this .factories = factories ;
399
+ }
400
+
401
+ /**
402
+ * Create a {@link RepositoryFactoriesLoader} instance that will load and instantiate the factory implementations
403
+ * from the default location, using the given class loader.
404
+ *
405
+ * @param classLoader the ClassLoader to use for loading resources; can be {@code null} to use the default
406
+ * @return a {@link RepositoryFactoriesLoader} instance
407
+ * @see #forResourceLocation(String)
408
+ */
409
+ public static RepositoryFactoriesLoader forDefaultResourceLocation (@ Nullable ClassLoader classLoader ) {
410
+ ClassLoader resourceClassLoader = (classLoader != null ? classLoader
411
+ : SpringFactoriesLoader .class .getClassLoader ());
412
+ return new RepositoryFactoriesLoader (classLoader ,
413
+ loadFactoriesResource (resourceClassLoader , FACTORIES_RESOURCE_LOCATION ));
414
+ }
415
+
416
+ List <String > loadFactoryNames (String factoryType ) {
417
+ return this .factories .getOrDefault (factoryType , Collections .emptyList ());
418
+ }
419
+ }
323
420
}
0 commit comments