Skip to content

Commit 52bc16d

Browse files
authored
Merge pull request mybatis#359 from kazuki43zoo/support-wildcard-on-package-based-scan
Allow to specify a wildcard at typeAliasesPackage and typeHandlersPackage
2 parents 5d63dc6 + bb11820 commit 52bc16d

File tree

2 files changed

+81
-26
lines changed

2 files changed

+81
-26
lines changed

src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,21 @@
1515
*/
1616
package 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;
2419
import java.io.IOException;
20+
import java.lang.reflect.Modifier;
2521
import java.sql.SQLException;
22+
import java.util.HashSet;
2623
import java.util.Optional;
2724
import java.util.Properties;
25+
import java.util.Set;
2826
import java.util.stream.Stream;
2927

30-
import javax.sql.DataSource;
31-
3228
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
3329
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
3430
import org.apache.ibatis.cache.Cache;
3531
import org.apache.ibatis.executor.ErrorContext;
32+
import org.apache.ibatis.io.Resources;
3633
import org.apache.ibatis.io.VFS;
3734
import org.apache.ibatis.mapping.DatabaseIdProvider;
3835
import org.apache.ibatis.mapping.Environment;
@@ -55,7 +52,19 @@
5552
import org.springframework.context.event.ContextRefreshedEvent;
5653
import org.springframework.core.NestedIOException;
5754
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;
5860
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;
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
}

src/test/java/org/mybatis/spring/SqlSessionFactoryBeanTest.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ void testAddATypeAlias() throws Exception {
341341
@Test
342342
void testSearchATypeAliasPackage() throws Exception {
343343
setupFactoryBean();
344-
factoryBean.setTypeAliasesPackage("org/mybatis/spring/type");
344+
factoryBean.setTypeAliasesPackage("org.mybatis.spring.type");
345345

346346
TypeAliasRegistry typeAliasRegistry = factoryBean.getObject().getConfiguration().getTypeAliasRegistry();
347347
typeAliasRegistry.resolveAlias("testAlias");
@@ -354,7 +354,7 @@ void testSearchATypeAliasPackage() throws Exception {
354354
void testSearchATypeAliasPackageWithSuperType() throws Exception {
355355
setupFactoryBean();
356356
factoryBean.setTypeAliasesSuperType(SuperType.class);
357-
factoryBean.setTypeAliasesPackage("org/mybatis/spring/type");
357+
factoryBean.setTypeAliasesPackage("org.mybatis.*.type");
358358

359359
TypeAliasRegistry typeAliasRegistry = factoryBean.getObject().getConfiguration().getTypeAliasRegistry();
360360
typeAliasRegistry.resolveAlias("testAlias2");
@@ -364,10 +364,32 @@ void testSearchATypeAliasPackageWithSuperType() throws Exception {
364364
assertThrows(TypeException.class, () -> typeAliasRegistry.resolveAlias("dummyTypeHandler"));
365365
}
366366

367+
@Test
368+
void testSearchATypeAliasPackageWithSamePackage() throws Exception {
369+
setupFactoryBean();
370+
factoryBean.setTypeAliasesPackage("org.mybatis.spring.type, org.*.spring.type");
371+
372+
TypeAliasRegistry typeAliasRegistry = factoryBean.getObject().getConfiguration().getTypeAliasRegistry();
373+
typeAliasRegistry.resolveAlias("testAlias");
374+
typeAliasRegistry.resolveAlias("testAlias2");
375+
typeAliasRegistry.resolveAlias("dummyTypeHandler");
376+
typeAliasRegistry.resolveAlias("superType");
377+
}
378+
367379
@Test
368380
void testSearchATypeHandlerPackage() throws Exception {
369381
setupFactoryBean();
370-
factoryBean.setTypeHandlersPackage("org/mybatis/spring/type");
382+
factoryBean.setTypeHandlersPackage("org.**.type");
383+
384+
TypeHandlerRegistry typeHandlerRegistry = factoryBean.getObject().getConfiguration().getTypeHandlerRegistry();
385+
assertThat(typeHandlerRegistry.hasTypeHandler(BigInteger.class)).isTrue();
386+
assertThat(typeHandlerRegistry.hasTypeHandler(BigDecimal.class)).isTrue();
387+
}
388+
389+
@Test
390+
void testSearchATypeHandlerPackageWithSamePackage() throws Exception {
391+
setupFactoryBean();
392+
factoryBean.setTypeHandlersPackage("org.mybatis.spring.type, org.mybatis.*.type");
371393

372394
TypeHandlerRegistry typeHandlerRegistry = factoryBean.getObject().getConfiguration().getTypeHandlerRegistry();
373395
assertThat(typeHandlerRegistry.hasTypeHandler(BigInteger.class)).isTrue();

0 commit comments

Comments
 (0)