Mybatis的别名问题typeAliases(mybatis3.1.0 VS mybatis3.4.5)

背景:  项目进行架构升级,将mybatis从3.1.0升级到3.4.5,mybatis-spring从1.1.0升级到1.3.1

问题

2018-01-10 08:47:59.695 ERROR org.springframework.web.context.ContextLoader 350 initWebApplicationContext - Context initialization failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bankSubscribeService': Unsatisfied dependency expressed through field 'requestappMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'requestappMapper' defined in URL [jar:file:/D:/Idea_workspace/web_workspace/bbb-core-web/target/bbb-core/WEB-INF/lib/bbb-core-dao-1.2.0-SNAPSHOT.jar!/com/hhh/etrade/bbb/dao/trade/RequestappMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in URL [jar:file:/D:/Idea_workspace/web_workspace/bbb-core-web/target/bbb-core/WEB-INF/lib/bbb-core-dao-1.2.0-SNAPSHOT.jar!/spring/spring-bbb-core-dao.xml]: Invocation of init method failed; nested exception is org.apache.ibatis.type.TypeException: The alias 'City' is already mapped to the value 'com.htffund.etrade.inst.model.bank.City'.
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:443) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:325) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) ~[spring-web-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5118) ~[catalina.jar:7.0.73]
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5634) ~[catalina.jar:7.0.73]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145) ~[catalina.jar:7.0.73]
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:899) ~[catalina.jar:7.0.73]
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:875) ~[catalina.jar:7.0.73]
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652) ~[catalina.jar:7.0.73]
	at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1863) ~[catalina.jar:7.0.73]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
	at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) ~[tomcat-coyote.jar:7.0.73]
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) ~[?:1.8.0_101]
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) ~[?:1.8.0_101]
	at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618) ~[catalina.jar:7.0.73]
	at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565) ~[catalina.jar:7.0.73]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
	at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) ~[tomcat-coyote.jar:7.0.73]
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) ~[?:1.8.0_101]
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) ~[?:1.8.0_101]
	at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468) ~[?:1.8.0_101]
	at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76) ~[?:1.8.0_101]
	at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309) ~[?:1.8.0_101]
	at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1401) ~[?:1.8.0_101]
	at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829) ~[?:1.8.0_101]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:324) ~[?:1.8.0_101]
	at sun.rmi.transport.Transport$1.run(Transport.java:200) ~[?:1.8.0_101]
	at sun.rmi.transport.Transport$1.run(Transport.java:197) ~[?:1.8.0_101]
	at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_101]
	at sun.rmi.transport.Transport.serviceCall(Transport.java:196) ~[?:1.8.0_101]
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568) ~[?:1.8.0_101]
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826) ~[?:1.8.0_101]
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683) ~[?:1.8.0_101]
	at java.security.AccessController.doPrivileged(Native Method) [?:1.8.0_101]
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682) [?:1.8.0_101]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_101]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_101]
	at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101]

思考:看异常信息应该是说这个别名已经存在了

          (1). 为啥没升级的时候没问题呢?

          (2). 虽然项目中存在多个同名的类,但是出于不同的包中啊

解决途径

         (1). Google了好久,看了mybatis-spring的官方文档也不行,好久之后才找到这篇 https://my.oschina.net/zimingforever/blog/478137  但是发现该文中的源码和mybatis3.1.0是一样的,按这个源码应该不会报错才对。

          (2). 对比mybatis从3.1.0和3.4.5关于设置类别名的源码发现为什么会出现这个异常了

先说下mybatis配置别名的方法,共有两种:

方法1:指定一个具体的类的别名

在mapper.xml文件中配置


 
  

3.1.0版本的实现

public void registerAlias(String alias, Class value) {    
    assert alias != null;   
    // 把别名转为小写字母
    String key = alias.toLowerCase();   
    // 如果key或者alias如果已经添加到了map中,且对应的class和已添加的不一致则抛出异常
    if (TYPE_ALIASES.containsKey(key) && !TYPE_ALIASES.get(key).equals(value.getName()) && TYPE_ALIASES.get(alias) != null) {      
        if (!value.equals(TYPE_ALIASES.get(alias))) {        
            throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(alias).getName() + "'.");
      }
    }    
    // 否则添加到map中    
    TYPE_ALIASES.put(key, value);
  }

3.4.5版本的实现

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 (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }
区别主要是黄色的这两处,3.1.0通过alias从map中获取,要知道map中的key都设置为全小写的了,而alias却是简单类名(不带包前缀),所以不管怎样 TYPE_ALIASES.get(alias) == null,3.1.0不会抛出异常。但是会对key对应的值进行覆盖。

方法2:指定一个包中的所有的类的别名,省去一个个设置


        
        
		
        
我遇到的问题出在这里,相同类名两个类的包前缀都是com.test.etrade.abc.model
 
方法2的实现在3.1.0和3.4.5没有很大区别:

public void registerAliases(String packageName){    
    // 第二个会在mybatis的io去寻找对应的类时会用到    
    // 因为是把这个包中所有的类都进行别名注册,因而用的的Object类
    registerAliases(packageName, Object.class);
}  

public void registerAliases(String packageName, Class superType){
    ResolverUtil resolverUtil = new ResolverUtil();    
    // mybatis的io去包中寻找对应的类,因为是把这个包中所有的类都进行别名注册    
    // 因而此时会将所有的类都读到
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set> typeSet = resolverUtil.getClasses();    
    for(Class type : typeSet){      
        //Ignore inner classes and interfaces (including package-info.java)     
        // 忽略内部类和接口
        if (!type.isAnonymousClass() && !type.isInterface()) {
          registerAlias(type);
        }
    }
  }
  
 public void registerAlias(Class type) {    
    // 利用反射机制得到这个类的不包含包名的简单名称
    String alias = type.getSimpleName();    
    // 如果这个类有Alias这个注解,获取这个注解的值作为别名,否则就用自己的原始类名
    Alias aliasAnnotation = (Alias) type.getAnnotation(Alias.class);    
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    // 开始注册,这个步骤使用的是第一种方法
    registerAlias(alias, type);
  }

解决方案

     (1). 不取同名的类,即使在不同的包中

     (2). 采用第一种别名配置方法,对不同包中的同名类分别设置不同的别名

     (3). 用注解@Alias在类上配置别名。

注: 当然别名都是不能相同的,即使是大小写不同也不行,因为别名最后都是转为全小写存到Map中的

TYPE_ALIASES.put(key, value)
。取类型别名主要是为了在mapper.xml中使用方便,不用每次都写那么长长的类全名,用简单的别名就可以了。



你可能感兴趣的:(java笔记,mybatis,mybatis-spirng,3.1.0,3.4.5,typealias)