1515 */
1616package org .mybatis .spring ;
1717
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 ;
2419import java .io .IOException ;
20+ import java .lang .reflect .Modifier ;
2521import java .sql .SQLException ;
22+ import java .util .HashSet ;
2623import java .util .Optional ;
2724import java .util .Properties ;
25+ import java .util .Set ;
2826import java .util .stream .Stream ;
2927
30- import javax .sql .DataSource ;
31-
3228import org .apache .ibatis .builder .xml .XMLConfigBuilder ;
3329import org .apache .ibatis .builder .xml .XMLMapperBuilder ;
3430import org .apache .ibatis .cache .Cache ;
3531import org .apache .ibatis .executor .ErrorContext ;
32+ import org .apache .ibatis .io .Resources ;
3633import org .apache .ibatis .io .VFS ;
3734import org .apache .ibatis .mapping .DatabaseIdProvider ;
3835import org .apache .ibatis .mapping .Environment ;
5552import org .springframework .context .event .ContextRefreshedEvent ;
5653import org .springframework .core .NestedIOException ;
5754import 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 ;
5860import 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 ;
5968
6069/**
6170 * {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}.
@@ -79,6 +88,9 @@ public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, In
7988
8089 private static final Logger LOGGER = LoggerFactory .getLogger (SqlSessionFactoryBean .class );
8190
91+ private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver ();
92+ private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory ();
93+
8294 private Resource configLocation ;
8395
8496 private Configuration configuration ;
@@ -211,6 +223,8 @@ public void setPlugins(Interceptor[] plugins) {
211223 /**
212224 * Packages to search for type aliases.
213225 *
226+ * <p>Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.model}.
227+ *
214228 * @since 1.0.1
215229 *
216230 * @param typeAliasesPackage package to scan for domain objects
@@ -236,6 +250,8 @@ public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
236250 /**
237251 * Packages to search for type handlers.
238252 *
253+ * <p>Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.typehandler}.
254+
239255 * @since 1.0.1
240256 *
241257 * @param typeHandlersPackage package to scan for type handlers
@@ -416,9 +432,9 @@ public void afterPropertiesSet() throws Exception {
416432 * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
417433 *
418434 * @return SqlSessionFactory
419- * @throws IOException if loading the config file failed
435+ * @throws Exception if configuration is failed
420436 */
421- protected SqlSessionFactory buildSqlSessionFactory () throws IOException {
437+ protected SqlSessionFactory buildSqlSessionFactory () throws Exception {
422438
423439 final Configuration targetConfiguration ;
424440
@@ -444,13 +460,8 @@ protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
444460 Optional .ofNullable (this .vfs ).ifPresent (targetConfiguration ::setVfsImpl );
445461
446462 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 );
454465 }
455466
456467 if (!isEmpty (this .typeAliases )) {
@@ -468,12 +479,11 @@ protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
468479 }
469480
470481 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 );
477487 }
478488
479489 if (!isEmpty (this .typeHandlers )) {
@@ -571,4 +581,27 @@ public void onApplicationEvent(ApplicationEvent event) {
571581 }
572582 }
573583
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+
574607}
0 commit comments