15
15
*/
16
16
package org .mybatis .spring ;
17
17
18
- import static org .springframework .util .Assert .notNull ;
19
- import static org .springframework .util .Assert .state ;
20
- import static org .springframework .util .ObjectUtils .isEmpty ;
21
- import static org .springframework .util .StringUtils .hasLength ;
22
- import static org .springframework .util .StringUtils .tokenizeToStringArray ;
23
-
18
+ import javax .sql .DataSource ;
24
19
import java .io .IOException ;
20
+ import java .lang .reflect .Modifier ;
25
21
import java .sql .SQLException ;
22
+ import java .util .HashSet ;
26
23
import java .util .Optional ;
27
24
import java .util .Properties ;
25
+ import java .util .Set ;
28
26
import java .util .stream .Stream ;
29
27
30
- import javax .sql .DataSource ;
31
-
32
28
import org .apache .ibatis .builder .xml .XMLConfigBuilder ;
33
29
import org .apache .ibatis .builder .xml .XMLMapperBuilder ;
34
30
import org .apache .ibatis .cache .Cache ;
35
31
import org .apache .ibatis .executor .ErrorContext ;
32
+ import org .apache .ibatis .io .Resources ;
36
33
import org .apache .ibatis .io .VFS ;
37
34
import org .apache .ibatis .mapping .DatabaseIdProvider ;
38
35
import org .apache .ibatis .mapping .Environment ;
55
52
import org .springframework .context .event .ContextRefreshedEvent ;
56
53
import org .springframework .core .NestedIOException ;
57
54
import org .springframework .core .io .Resource ;
55
+ import org .springframework .core .io .support .PathMatchingResourcePatternResolver ;
56
+ import org .springframework .core .io .support .ResourcePatternResolver ;
57
+ import org .springframework .core .type .ClassMetadata ;
58
+ import org .springframework .core .type .classreading .CachingMetadataReaderFactory ;
59
+ import org .springframework .core .type .classreading .MetadataReaderFactory ;
58
60
import org .springframework .jdbc .datasource .TransactionAwareDataSourceProxy ;
61
+ import org .springframework .util .ClassUtils ;
62
+
63
+ import static org .springframework .util .Assert .notNull ;
64
+ import static org .springframework .util .Assert .state ;
65
+ import static org .springframework .util .ObjectUtils .isEmpty ;
66
+ import static org .springframework .util .StringUtils .hasLength ;
67
+ import static org .springframework .util .StringUtils .tokenizeToStringArray ;
59
68
60
69
/**
61
70
* {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}.
@@ -79,6 +88,9 @@ public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, In
79
88
80
89
private static final Logger LOGGER = LoggerFactory .getLogger (SqlSessionFactoryBean .class );
81
90
91
+ private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver ();
92
+ private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory ();
93
+
82
94
private Resource configLocation ;
83
95
84
96
private Configuration configuration ;
@@ -211,6 +223,8 @@ public void setPlugins(Interceptor[] plugins) {
211
223
/**
212
224
* Packages to search for type aliases.
213
225
*
226
+ * <p>Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.model}.
227
+ *
214
228
* @since 1.0.1
215
229
*
216
230
* @param typeAliasesPackage package to scan for domain objects
@@ -236,6 +250,8 @@ public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
236
250
/**
237
251
* Packages to search for type handlers.
238
252
*
253
+ * <p>Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.typehandler}.
254
+
239
255
* @since 1.0.1
240
256
*
241
257
* @param typeHandlersPackage package to scan for type handlers
@@ -416,9 +432,9 @@ public void afterPropertiesSet() throws Exception {
416
432
* Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
417
433
*
418
434
* @return SqlSessionFactory
419
- * @throws IOException if loading the config file failed
435
+ * @throws Exception if configuration is failed
420
436
*/
421
- protected SqlSessionFactory buildSqlSessionFactory () throws IOException {
437
+ protected SqlSessionFactory buildSqlSessionFactory () throws Exception {
422
438
423
439
final Configuration targetConfiguration ;
424
440
@@ -444,13 +460,8 @@ protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
444
460
Optional .ofNullable (this .vfs ).ifPresent (targetConfiguration ::setVfsImpl );
445
461
446
462
if (hasLength (this .typeAliasesPackage )) {
447
- String [] typeAliasPackageArray = tokenizeToStringArray (this .typeAliasesPackage ,
448
- ConfigurableApplicationContext .CONFIG_LOCATION_DELIMITERS );
449
- Stream .of (typeAliasPackageArray ).forEach (packageToScan -> {
450
- targetConfiguration .getTypeAliasRegistry ().registerAliases (packageToScan ,
451
- typeAliasesSuperType == null ? Object .class : typeAliasesSuperType );
452
- LOGGER .debug (() -> "Scanned package: '" + packageToScan + "' for aliases" );
453
- });
463
+ scanClasses (this .typeAliasesPackage , this .typeAliasesSuperType )
464
+ .forEach (targetConfiguration .getTypeAliasRegistry ()::registerAlias );
454
465
}
455
466
456
467
if (!isEmpty (this .typeAliases )) {
@@ -468,12 +479,11 @@ protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
468
479
}
469
480
470
481
if (hasLength (this .typeHandlersPackage )) {
471
- String [] typeHandlersPackageArray = tokenizeToStringArray (this .typeHandlersPackage ,
472
- ConfigurableApplicationContext .CONFIG_LOCATION_DELIMITERS );
473
- Stream .of (typeHandlersPackageArray ).forEach (packageToScan -> {
474
- targetConfiguration .getTypeHandlerRegistry ().register (packageToScan );
475
- LOGGER .debug (() -> "Scanned package: '" + packageToScan + "' for type handlers" );
476
- });
482
+ scanClasses (this .typeHandlersPackage , TypeHandler .class ).stream ()
483
+ .filter (clazz -> !clazz .isInterface ())
484
+ .filter (clazz -> !Modifier .isAbstract (clazz .getModifiers ()))
485
+ .filter (clazz -> ClassUtils .getConstructorIfAvailable (clazz ) != null )
486
+ .forEach (targetConfiguration .getTypeHandlerRegistry ()::register );
477
487
}
478
488
479
489
if (!isEmpty (this .typeHandlers )) {
@@ -571,4 +581,27 @@ public void onApplicationEvent(ApplicationEvent event) {
571
581
}
572
582
}
573
583
584
+ private Set <Class <?>> scanClasses (String packagePatterns , Class <?> assignableType )
585
+ throws IOException {
586
+ Set <Class <?>> classes = new HashSet <>();
587
+ String [] packagePatternArray = tokenizeToStringArray (packagePatterns ,
588
+ ConfigurableApplicationContext .CONFIG_LOCATION_DELIMITERS );
589
+ for (String packagePattern : packagePatternArray ) {
590
+ Resource [] resources = RESOURCE_PATTERN_RESOLVER .getResources (ResourcePatternResolver .CLASSPATH_ALL_URL_PREFIX +
591
+ ClassUtils .convertClassNameToResourcePath (packagePattern ) + "/**/*.class" );
592
+ for (Resource resource : resources ) {
593
+ try {
594
+ ClassMetadata classMetadata = METADATA_READER_FACTORY .getMetadataReader (resource ).getClassMetadata ();
595
+ Class <?> clazz = Resources .classForName (classMetadata .getClassName ());
596
+ if (assignableType == null || assignableType .isAssignableFrom (clazz )) {
597
+ classes .add (clazz );
598
+ }
599
+ } catch (Throwable e ) {
600
+ LOGGER .warn (() -> "Cannot load the '" + resource + "'. Cause by " + e .toString ());
601
+ }
602
+ }
603
+ }
604
+ return classes ;
605
+ }
606
+
574
607
}
0 commit comments