Springboot集成Mybatis在不同文件夹下出现同名文件时启动报错

问题描述

项目框架springboot+mybatisplus+mybatis,需求中使用webservice,经过各种研究、对比、测试最后决定使用wsimport生成代码。wsimport是jdk自带的webservice客户端工具,可以根据wsdl文档生成客户端调用代码(java代码)位于JAVA_HOME/bin目录下。

wsimport [options] <WSDL_URI>

生成的文件如下:

每个webservice生成的代码都有一些同名的类,如DtZrsv、ObjectFactory,当Springboot启动时悲剧发生了,一长串的异常,整个世界都不好了。

Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.apache.ibatis.type.TypeException: The alias 'ObjectFactory' is already mapped to the value 'com.example.webservice.ObjectFactory'.

解决路程

看到这个错误,下意识的认为是命名重复,bean工厂实例化的问题,接下来对着文件名和类名一通操作。。。

经过一通折腾还是没有解决问题,于是决定跟踪源码看看到底是啥原因。首先根据错误信息找到MybatisPlusAutoConfiguration类,并在sqlSessionFactory方法设下断点

经过第一轮调试初步确定错误实在方法最后一行return factory.getObject()出错,

@Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        .......
        .......
        return factory.getObject();
    }

facotry.getObject方法

    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            afterPropertiesSet(); // 关键代码
        }

        return this.sqlSessionFactory;
    }



public void afterPropertiesSet() throws Exception {
        notNull(dataSource, "Property 'dataSource' is required");
        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
            "Property 'configuration' and 'configLocation' can not specified with together");
        SqlRunner.DEFAULT.close();
        this.sqlSessionFactory = buildSqlSessionFactory(); // 关键代码
    }

接下来很容易就找到buildSqlSessionFactory,看名称就知道这个地方应该是创建sqlSessionFactory的方法,继续跟踪查找元凶,在单步调试的帮助下找到错误行

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

        final Configuration targetConfiguration;

        ......
        ......
        if (hasLength(this.typeAliasesPackage)) {
            scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
                .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
                .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias); //关键代码
        } 

public void registerAlias(Class<?> type) {
    String alias = type.getSimpleName();
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    }
    registerAlias(alias, type); //关键代码
  }

public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    typeAliases.put(key, value);
  }

发现在 targetConfiguration.getTypeAliasRegistry()::registerAlias 产生错误,追踪下去直到registerAlias(String alias, Class<?> value)方法,发现了报错的地方,从代码中可以看出mybatis加载类型别名时,会将别名存储在个Map<String,Class<?>>中,而map内部插入一个key相同的键值对是一定不行的,因此当存在相同类名时会出错。

解决办法

在代码中可以看出配置了typeAliasesPackage才会进入这个方法,因此目前使用最简单粗暴将其配置去除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苜蓿花乐园

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值