在前边的博客在分析了mybatis解析settings标签,《mybatis源码配置文件解析之二:解析settings标签》。下面来看解析typeAliases标签的过程。

一、概述

在mybatis核心配置文件(mybatis-config.xml)中有关typeAliases的配置如下,

<typeAliases>
<package name="cn.com.mybatis.bean"></package>
<typeAlias name="user" type="cn.com.mybatis.bean.User"></typeAlias>
</typeAliases>

上面给出了两种配置typeAlias的放式,一种是配置package标签,一种是typeAlias表。

我上面的配置是有问题的,在测试的时候一直报下面的错误,

上面的问题困扰了笔者好久,没找到原因,因为解析typeAliases标签的源码中找不到任何的原因,最后排查日志,原来是在加载核心配置文件的时候要把配置和mybatis的dtd文件进行验证,这里是验证出错了,具体的错误是typeAlias标签必须在package标签的前边,也就是标签是有顺序的。把配置改为下面的顺序,程序正常,

<typeAliases>
<typeAlias alias="user" type="cn.com.mybatis.bean.User"></typeAlias>
<package name="cn.com.mybatis.bean"/>
</typeAliases>

1、配置<package>标签

<package>标签配置的是一个包名,mybatis会扫描该包下的所有类,并注册一个别名,这里在标签中无法为某个类指定一个自定义的别名,mybatis提供了另外一种方式可以使用自定义的别名,即@Alias注解,在类上标记该注解,如下,

package cn.com.mybatis.bean;

import org.apache.ibatis.type.Alias;

//配置别名为myMenu
@Alias(value="myMenu")
public class Menu { private String menuId;
private String menuName;
private String url;
}

上面为Menu类配置了别名,在扫描该包的时候会使用自定义的别名,不会使用mybatis默认的别名规则(Class.getSimpleName())

2、配置<typeAlias>标签

这种配置是单独为某个类配置别名,其中alias属性可以不配置,不配置则使用mybatis默认的别名规则,如下

<typeAlias alias="MyUser" type="cn.com.mybatis.bean.User"></typeAlias>

上面看了typeAlias的两种配置方式,那么何为typeAlias,意思就是给一个类配置一个别名,如这里有一个cn.com.mybatis.bean.User类,可以为其配置别名为MyUser,

那么在配置文件中便可以使用别名代替类的全限类名,目的是简便。这里需要注意的是配置的别名的使用范围仅限于mybatis的配置文件中(包含核心配置文件和Mpper映射文件)

二、详述

上面,了解了typeAlias的配置及作用,下面看mybatis是如何解析的。

在XMLConfigBuilder类中的parseConfiguration方法,

  private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析properties标签
propertiesElement(root.evalNode("properties"));
//解析settings标签,1、把<setting>标签解析为Properties对象
Properties settings = settingsAsProperties(root.evalNode("settings"));
/*2、对<settings>标签中的<setting>标签中的内容进行解析,这里解析的是<setting name="vfsImpl" value=",">
* VFS是mybatis中用来表示虚拟文件系统的一个抽象类,用来查找指定路径下的资源。上面的key为vfsImpl的value可以是VFS的具体实现,必须
* 是权限类名,多个使用逗号隔开,如果存在则设置到configuration中的vfsImpl属性中,如果存在多个,则设置到configuration中的仅是最后一个
* */
loadCustomVfs(settings);
//解析别名标签,例<typeAlias alias="user" type="cn.com.bean.User"/>
typeAliasesElement(root.evalNode("typeAliases"));
//解析插件标签
pluginElement(root.evalNode("plugins"));
//解析objectFactory标签,此标签的作用是mybatis每次创建结果对象的新实例时都会使用ObjectFactory,如果不设置
//则默认使用DefaultObjectFactory来创建,设置之后使用设置的
objectFactoryElement(root.evalNode("objectFactory"));
//解析objectWrapperFactory标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析reflectorFactory标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析environments标签
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//解析<mappers>标签
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

从上面可以看出typeAliasesElement方法,此方法用来解析typeAliases标签及其子标签,

private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//1、解析package标签
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
//2、解析typeAlias标签
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);
}
}
}
}
}

typeAliasesElement方法会分别解析typeAliases标签的package和typeAlias子标签。通过上面的分析知道在配置的时候<typeAlias>标签要在<package>标签前边,但这里按照源码的顺序先分析<package>标签的解析。

1、解析<package>标签

下面看typeAliasesElement方法中对package标签的解析,

if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
}

从上面可以看到获取<package>标签的name属性,也就配置的包名,然后调用下面的方法,

configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);

可以看到从Configuration中获得TypeAliasRegistry,然后调用其registerAliases方法,

public void registerAliases(String packageName){
registerAliases(packageName, Object.class);
}

又调用另外一个方法,如下,

/**
*
* 为包下的所有java bean注册别名
* @param packageName
* @param superType
*/
public void registerAliases(String packageName, Class<?> superType){
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
//把该包下的所有类进行加载,把其Class对象放到resolverUtil的matches中
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
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);
}
}
}

上面方法的作用是遍历给的的包名,把该包下的所有的类进行加载,并放到resolverUtil中的matches中,这里具体的遍历方法暂且不看。遍历完成后取出resolverUtil中的所有Class对象,只要不是匿名类、接口则执行registerAlias方法,

public void registerAlias(Class<?> type) {
//获得类的简单类名,如cn.com.mybatis.bean.User 则其简单名称为User
String alias = type.getSimpleName();
//判断类上是否存在@Alias注解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
//如果存在@Alias注解,则使用注解上配置的value属性作为别名
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}

看上面的方法,上面的方法先获得Class的简单类名,

//获得类的简单类名,如cn.com.mybatis.bean.User 则其简单名称为User
String alias = type.getSimpleName();

然后会判断类上是否有@Alias注解,如果有则取其value值作为类的别名,

//判断类上是否存在@Alias注解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
//如果存在@Alias注解,则使用注解上配置的value属性作为别名
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}

进行上面的判断,存在@Alias注解,使用其value值作为别名,否则使用类的简单类名(Class.getSimpleName()),然后执行registerAlias方法,

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);
}

上面的代码会把别名转化为英文的小写作为存入的key,使用对应的Class存入TYPE_ALIASES中。如果已经注册过该key则会抛出异常,也就是不允许重复注册或者相同的key是无法覆盖的。这里还有一个问题,如果我们配置的是别名中含有大写,那么注册的时候是小写的,在使用的时候是用配置的还是用注册的,例,上面的例子,

package cn.com.mybatis.bean;

import org.apache.ibatis.type.Alias;

//配置别名为myMenu
@Alias(value="myMenu")
public class Menu { private String menuId;
private String menuName;
private String url;
}

这里配置的是myMenu,注册的确实下面的

可以看到注册之后的是mymenu。其实在使用的时候是大小写不敏感的,在匹配的时候会统一转化为小写,这样就可以对应TYPE_ALIASES中已注册的别名。

2、解析<typeAlias>标签

上面分析了<package>标签的解析过程,下面看有关<typeAlias>标签的解析,

解析<typeAlias>标签即是获取alias和type两个属性,可以看到对alias进行了判断,也就说可以不配置alias属性,那么会使用下面的方法处理

public void registerAlias(Class<?> type) {
//获得类的简单类名,如cn.com.mybatis.bean.User 则其简单名称为User
String alias = type.getSimpleName();
//判断类上是否存在@Alias注解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
//如果存在@Alias注解,则使用注解上配置的value属性作为别名
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}

该方法前面已分析,会判断配置的类是否含有@Alias注解,如果有则使用注解上的value值。这里存在一个问题,如果在<typeAlias>标签中配置了alias,在类上也有@Alias注解,且不一样,以哪个为准,通过上面的分析,得出下面的结论,

在使用<typeAlias alias="myAlias">标签的时候,配置了alias属性,在类上也有@Alias(value="myAlias2"),已配置的为准(最终别名为myAlias)

下面看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 (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);
}

此方法上面分析过,如果存在相同的key会抛异常,最终存入TYPE_ALIASES中。

三、总结

本文分析了mybatis核心配置文件(mybatis-config.xml)的<typeAlias>标签的配置及源码解析。

另在写Mapper映射文件和核心配置文件的时候会使用一些自定义的别名,这些别名是怎么注册的那,在Configuration、TypeAliasRegistry类中进行了注册,如下Configuration,

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);
}

在TypeAliasRegistry中注册了下面的别名,

//默认的构造方法,初始化系统内置的别名
public TypeAliasRegistry() {
registerAlias("string", String.class); registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class); registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class); registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class);
}

上面两个类注册了系统内置的别名,在核心配置文件和Mapper映射文件中可使用,mybatis会自动映射其注册类型,且大小写不区分。

原创不易,有不正之处欢迎指正。

mybatis源码配置文件解析之三:解析typeAliases标签的更多相关文章

  1. mybatis源码配置文件解析之四:解析plugins标签

    在前边的博客在分析了mybatis解析typeAliases标签,<mybatis源码配置文件解析之三:解析typeAliases标签>.下面来看解析plugins标签的过程. 一.概述 ...

  2. mybatis源码配置文件解析之二:解析settings标签

    在前边的博客中分析了mybatis解析properties标签,<mybatis源码配置文件解析之一:解析properties标签>.下面来看解析settings标签的过程. 一.概述 在 ...

  3. mybatis源码配置文件解析之五:解析mappers标签

    在上篇博客中分析了plugins标签,<mybatis源码配置文件解析之四:解析plugins标签>,了解了其使用方式及背后的原理.现在来分析<mappers>标签. 一.概述 ...

  4. mybatis源码配置文件解析之五:解析mappers标签(解析XML映射文件)

    在上篇文章中分析了mybatis解析<mappers>标签,<mybatis源码配置文件解析之五:解析mappers标签>重点分析了如何解析<mappers>标签中 ...

  5. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  6. Spring mybatis源码篇章-MybatisDAO文件解析(一)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...

  7. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

  8. mybatis源码配置文件解析之一:解析properties标签

    mybatis作为日常开发的常用ORM框架,在开发中起着很重要的作用,了解其源码对日常的开发有很大的帮助.源码版本为:3-3.4.x,可执行到github进行下载. 从这篇文章开始逐一分析mybati ...

  9. mybatis源码配置文件解析之五:解析mappers标签流程图

    前面几篇博客分析了mybatis解析mappers标签的过程,主要分为解析package和mapper子标签.补充一张解析的总体过程流程图,画的不好,多多谅解,感谢.

随机推荐

  1. linux pdftk

    部分内容来源网络,如有版权问题,请联系删除: http://xuqin.blog.51cto.com/5183168/1117780,   http://blog.sina.com.cn/s/blog ...

  2. 解决使用requests_html模块,req.html.render()下载chromium速度慢问题

    1.第一步,代码如下: from requests_html import HTMLSession url="https://www.baidu.com/" headers={ & ...

  3. 计算智能(CI)之粒子群优化算法(PSO)(一)

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! 计算智能(Computational Intelligence , ...

  4. windows10远程桌面,出现“出现身份验证错误 要求的函数不受支持...”等错误解决方法

    windows家庭普通版,更新补丁后无法远程连接windows server2012,出现以下报错: 解决方法: 1.win + R打开运行,输入 regedit,回车进入注册表 2.找到以下路径 \ ...

  5. Lack of free swap space on Zabbix server

    在模板(Template)里找到Linux OS模板,修改触发器 配置>模板>Template OS Linux>触发器 找到swap关键字 修改 {Template OS Linu ...

  6. IOS 获取系统时间戳

    IOS 获取系统时间戳常用方法 通用方法有如下三种: NSLog(); NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:]; NSTimeInte ...

  7. Java中的集合类、Lambda、鲁棒性简述

    集合类 在java.util包中提供了一些集合类,常用的有List.Set和Map类,其中List类和Set类继承了Collection接口.这些集合类又称为容器,长度是可变的,数组用来存放基本数据类 ...

  8. Chart控件的使用

    1.开启滚动条(ScaleView.Size除以Interval ,大概是中间出现纵线的数量,一般动ScaleView.Size) chart1.ChartAreas[0].CursorX.AutoS ...

  9. POJ 3273Monthly Expense(二分答案)

    题目链接 思路如下 题意:这一题让我们在一个 n 个数的序列,分成连续的的 m个子串(一个数也可是一个子串),是在所有子串中 和最大的子串 的和最小. 思路:我们可以用 二分法 来一个一个枚举答案,二 ...

  10. 从一个慢查询到MySQL字符集编码

    从一个慢查询到MySQL字符集编码 目录 从一个慢查询到MySQL字符集编码 1. 问题起源 2. MySQL字符集和字符集排序规则 2.1 字符集相关概念 2.2 MySQL中的字符集和字符集排序规 ...