一、背景
SpringBoot项目,使用了Mybatis-plus的MybatisSqlSessionFactoryBean去生成SqlSessionFactory的Bean,其中设置了TypeAliasesPackage,在IDEA里直接运行可以正常启动,打包运行就报entity的别名找不到。
//配置sqlSessionFactory的bean,设置分页拦截配置
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("com.×××.×××.×××.entity");
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.addInterceptor(new PaginationInterceptor());
bean.setConfiguration(configuration);
return bean.getObject();
}
***mapper.xml里面 使用别名:
二、排查问题
maven将代码打包成jar,在idea内debug模式运行jar(添加JAR application即可):
debug进入
bean.getObject()
中;执行
afterPropertiesSet()
,启动调用buildSqlSessionFactory()
生成sqlSessionFactory,其中就有解析typeAliasesPackage的部分在往下,会使用DefaultVFS解析文件,DefaultVFS发现需要解析的是个jar,然后就开启jar的读取流,查看根据内部的文件夹,去匹配要扫描文件夹TypeAliasesPackage,然后读取文件夹下的class文件名,将其拼接到TypeAliasesPackage后面,反射生成class,存放到一个set中。
最后将Set中的Class维护到TypeAliasRegistry中的typeAliases中。typeAliases是个Map,key为小写的类名简称,value为Class对象。
问题出现在了第3步,在jar的debug运行时,DefaultVFS没有读取到匹配的包名,因为SpringBoot打的包目录结构如下,DefaultVFS匹配直接从第一级目录开始匹配,所以未找到。
而在IDEA中直接编译运行时,其读取的是target中的classes下的目录,就可以找到对应的包名,注册成功别名。
-BOOT-INF
-classes
-com.×××.×××.×××
-mapper
-log4j.xml
-application.yaml
-lib
-META-INF
-org.springframework.boot.loader
三、解决办法
在使用VFS读取文件是List<String> children = VFS.getInstance().list(path)
,在getInstance()中,优先使用了VFS.USER_IMPLEMENTATIONS
维护的VFS实例,这个可用通过VFS.addImplClass(Class<? extends VFS> clazz)
设置,其中Mybatis-plus与SpringBoot均提供了VFS的实现SpringBootVFS,在配置MybatisSqlSessionFactoryBean.getObject前设置VFS.addImplClass(new SpringBootVFS());
即可。