mybatis源码学习(六)

本文详细解析MyBatis的别名机制,包括三种设置方式:注解、配置文件包名和单独配置。介绍了MyBatis如何在启动时解析核心配置文件,通过反射创建类并保存到别名集合中,实现简化XML文件中类名引用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis加载typeAliases

上一篇文章讲完了mybatis启动的时候,加载settings,settings的加载是非常复杂的,通常对于应用来说,只需要默认配置就可以,如果需要根据本身的业务需要,对mybatis进一步优化的话,可以修改settings配置。
在加载完settings配置了之后,接着XMLConfigBuilder通过执行typeAliasesElement(root.evalNode(“typeAliases”))加载typeAliases配置,typeAliases是别名的意思,如果不配置typeAliases的话,在xml文件里面bean需要写上全路径名,例如com.gzwx.model.Leave。配置了别名之后,xml文件只需要写上相应的别名即可。
在mybatis的核心配置文件里面,别名的配置方式有2种。

<typeAliases>
		<!--扫描com.gzwx.mybatis.model包下面所有的bean,并为当前包以及下面所有的后代包的每一个类都起一个默认别名,默认小写-->
      <package name="com.gzwx.mybatis.model" />
</typeAliases>
<typeAliases>
	<!--为单独com.gzwx.mybatis.model.Leave这个bean起别名,别名就是type的值-->
     <typeAlias type="leave" alias="com.gzwx.mybatis.model.Leave"/>
</typeAliases>

为bean起别名了之后,那么在xml里面可以这样写

	<!--leave就是typeAliase里面的别名-->
	<select id="findLeaveById" resultType="leave">
        select name,startDate,endDate,memo from oa_leave where id = #{id}
    </select>

mybatis也可以通过直接注解的方式为单独的bean起别名,例如@Alias(“leave”)。接着看源码typeAliasesElement(root.evalNode(“typeAliases”)),从configuration里面获取到typeAliases节点,并通过typeAliasesElement解析该节点的内容

  private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      //判断typeAliases有没有package属性,如果有,那么执行该方法
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
        //如果没有,那么看有没有alias属性,找到所有的alias属性,并单独对一个一个的bean起别名
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

先看package方式,configuration.getTypeAliasRegistry()是获取别名的注册器,这个方法就是在Configuration里面的

public TypeAliasRegistry getTypeAliasRegistry() {
    return typeAliasRegistry;
  }

而configuration的typeAliasRegistry这个变量是在configuration初始化的时候
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
然后在构造函数的时候,设置了很多的注册器。

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

然后再看registerAliases方法,传入的是包名,它里面调用的是

public void registerAliases(String packageName, Class<?> superType){
	//创建一个resolverUtil对象,这个对象专门用来根据包名,通过调用VFS对象里面的反射机制获取到这个包名下所有的类文件
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    //找到这个包名下所有的类文件,如果这个类是Object类型的话,那么把类假如到resolverUtil的matches集合里面
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    //返回matches集合的内容
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        //执行注册别名的方法
        registerAlias(type);
      }
    }
  }

从上面的代码可以看到,在注册别名的时候,先是通过一系列反射,找到包名下所有的类文件,然后放到一个集合中,再遍历这个集合,对一个一个的类进行别名的注册。

  public void registerAlias(Class<?> type) {
   //获取类的名称
    String alias = type.getSimpleName();
    //获取这个类的Alias的注解,getAnnotition是jdk自带的方法,这个方法是根据传入的类获取到相关的注解
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    //如果这个注解存在,那么直接用注解里面的值,所以这里面就说明一个问题,注解的优先级是大于配置文件里面的
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }

	/**
	*这个方法是把类放到TYPE_ALIASES集合里面,key就是alias,value就是传入的value
	*
	**/
  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);
    //如果原来集合里面已经包含了这个key,那么直接报错
    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);
  }

至此,对于package别名的注册已经完成了,接下来看对于单独一个一个alias配置的注册过程

   //获取typeAliases节点下alias属性
   String alias = child.getStringAttribute("alias");
   //获取typeAliases节点下type属性
   String type = child.getStringAttribute("type");
    try {
      //type的形式是:com.gzwx.mybatis.model.Leave,根据这个字符串反射出类
      Class<?> clazz = Resources.classForName(type);
      //如果在配置文件里面写了type那么直接把type作为key传进去注册,如果没写,那么在registerAlias里面通过getSimpleName()方法获取到类名,以类名进行注册,这个注册方法跟上面包名注册是一样的。
      if (alias == null) {
        typeAliasRegistry.registerAlias(clazz);
      } else {
        typeAliasRegistry.registerAlias(alias, clazz);
      }
    } catch (ClassNotFoundException e) {
      throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
    }

到这里,关于别名的解析就完成了。对于别名的解析,有3种方法,第一种是注解的方法,这个优先级是最高的,第二种是通过核心配置里面的包名package批量设置,第三种是通过核心配置里面配置alias和type设置。mybatis在启动的时候,通过解析核心配置文件,反射出相应的类,作为value,如果设置了type就把type作为key,如果设置了注解,就把注解里面的value作为key,否则以类名作为key,保存到TYPE_ALIASES这个集合里面。TYPE_ALIASES是TypeAliasRegistry的一个变量,typeAliasRegistry是XMLConfigBuilder里面的一个变量,通过这样的方式完成了别名的设置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值