文章目录

  • 前言
  • 一、如何使用?
  • 二、源码分析
  • 总结

前言

  使用MyBatis开发过程中sql语句都需要参数或返回值,而且大部分都是自定义对象。参数或返回值可以使用parameterType和resultType设置,正常情况下是需要使用包名加类名来确定对象的,也可以使用别名来简化。接下来详细讲述别名。


一、如何使用?

  别名的配置分为两种,一种是通过包名配置,获取包下类信息根据类名简写作为别名。另一种直接根据类信息配置别名。接下来说明下mybatis中别名是如何使用的。(会使用的可以直接看第二节源码分析)
项目结构如下:

  mybatis-config.xml中的代码(后面简写成config.xml或主配置),以package和typeAlias设置了别名信息。

<configuration><!-- 别名设置 --><typeAliases><!-- 根据类信息配置别名 --><!-- 别名定义为uur --><typeAlias alias="uur" type="com.learn.mybatis.entity.param.UmsUserParam" /><!-- 根据包名配置别名 --><package name="com.learn.mybatis.entity" /></typeAliases>
</configuration>

  也可以使用@Alias对类进行别名设置,前提是需要先设置package或typeAlias,如果未设置package或typeAlias,@Alias注解将不会生效(后面对注解进行分析)。
使用@Alias注解如下:

@Data
// 别名定位为uup
@Alias("uup")
public class UmsUserParam{/*** 系统生成的客户编号*/private String no;/*** 姓名*/private String name;/*** 性别*/private String sex;/*** 年龄*/private Integer age;}

  对自定义别名进行使用。

<!-- 使用别名uup或uur  -->
<select id="select" parameterType="uup" resultMap="BaseResultMap" >select<include refid="column_info"/>from ums_user<where><if test="no!=null">and `no` = like concat(concat('%',#{no}),'%')</if><if test="name!=null">and `name` like concat(concat('%',#{name}),'%')</if><if test="age!=null">and `age` = {age}</if><if test="sex!=null">and `sex` = {sex}</if></where></select>

  config.xml与注解分别配置了不同的别名。
加载自定义别名如下:解析typeAlias

加载自定义别名如下:解析package

解析mapper.xml时使用别名如下:

  可以看到别名信息中存在uup和uur两个而且对应的类是相同的。uur是通过typeAlias配置添加到别名信息中的,uup则是通过package扫描下面的类然后通过Alias注解注册的别名。

二、源码分析

  TypeAliasRegistry类使用map保存别名与类的关系,初始化时添加了常用的别名信息。TypeAliasRegistry类中包含通过参数获取类信息和注册别名的方法。
下面是TypeAliasRegistry类信息:

public class TypeAliasRegistry {// map对象用来维护别名与类的关系private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();// 构造方法 注册默认的别名信息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);}// 根据参数获取类信息(存在别名则直接从别名map中获取,否则根据参数转换成对应的类)@SuppressWarnings("unchecked")// throws class cast exception as well if types cannot be assignedpublic <T> Class<T> resolveAlias(String string) {try {if (string == null) {return null;}// 将key统一转换成小写(保存别名时是小写)// issue #748String key = string.toLowerCase(Locale.ENGLISH);Class<T> value;// 别名map中是否包含该keyif (TYPE_ALIASES.containsKey(key)) {// 包含则直接从map中获取类信息value = (Class<T>) TYPE_ALIASES.get(key);} else {// 不包含则直接转换成类信息value = (Class<T>) Resources.classForName(string);}return value;} catch (ClassNotFoundException e) {throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);}}// 根据包名注册别名public void registerAliases(String packageName){registerAliases(packageName, Object.class);}// 根据包名和父类注册别名public void registerAliases(String packageName, Class<?> superType){// 解析工具ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();// 扫描包下的类并且是superType的子类resolverUtil.find(new ResolverUtil.IsA(superType), packageName);// 获取符合条件的类Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();for(Class<?> type : typeSet){// 跳过内部类、接口if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {// 将类注册到别名map中registerAlias(type);}}}// 将类信息注册到别名中(package解析和typeAlias只设置类信息时会调用该方法,所以Alias需要在package或typeAlias设置后使用)public void registerAlias(Class<?> type) {// 获取类名(不包含包名,简单类名)String alias = type.getSimpleName();// 获取类上注解Alias aliasAnnotation = type.getAnnotation(Alias.class);if (aliasAnnotation != null) {// 存在注解则获取注解配置的别名alias = aliasAnnotation.value();} // 将别名和类信息注册到别名map中registerAlias(alias, type);}// 将别名和类信息添加到别名map中(package和typeAlias最终都要调用该方法)public void registerAlias(String alias, Class<?> value) {if (alias == null) {throw new TypeException("The parameter alias cannot be null");}// issue #748// 别名统一转换成小写(获取时传入的key同样操作)String key = alias.toLowerCase(Locale.ENGLISH);// 别名map中存在相同的key 且 对应的value不为null而且与存在的类信息不同时抛出异常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() + "'.");}// 将别名与类信息添加到别名map中TYPE_ALIASES.put(key, value);}// 添加别名的重载方法 通过stirng转换成对应的类信息在进行添加public void registerAlias(String alias, String value) {try {registerAlias(alias, Resources.classForName(value));} catch (ClassNotFoundException e) {throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);}}// 获取别名map 只读public Map<String, Class<?>> getTypeAliases() {return Collections.unmodifiableMap(TYPE_ALIASES);}
}

  TypeAliasRegistry是如何创建的?Mybatis的主配置类Configuration中包含TypeAliasRegistry属性。mybatis启动时创建Configuration对象,Configuration构造方法里创建TypeAliasRegistry对象。
代码信息如下:

 // Configuration无参构造,其他构造都会调用无参构造。(与别名无关的代码都删掉了)public Configuration() {// 创建TypeAliasRegistrythis.typeAliasRegistry = new TypeAliasRegistry();// 创建默认的别名信息this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);this.typeAliasRegistry.registerAlias("LRU", LruCache.class);this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);}

  用户自定义的别名是如何注册的呢?这里需要说明Configuration的解析了,以Xml形式为例,XMLConfigBuilder的parseConfiguration方法是对config.xml进行解析的,里面包含对别名的解析。
parseConfiguration方法如下:

// 对Config.xml文件进行解析(这里只看别名)private void parseConfiguration(XNode root) {try {//issue #117 read properties firstpropertiesElement(root.evalNode("properties"));Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);// 对别名进行解析typeAliasesElement(root.evalNode("typeAliases"));// mybatis插件解析pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));// 自定义的typeHandler进行解析typeHandlerElement(root.evalNode("typeHandlers"));// 对配置的mapper进行解析mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}// 对Config.xml文件中配置的别名进行解析
private void typeAliasesElement(XNode parent) {if (parent != null) {// 遍历typeAliases下子节点for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {// package解析String typeAliasPackage = child.getStringAttribute("name");configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);} else {// typeAlias进行解析// 获取定义的别名String alias = child.getStringAttribute("alias");// 获取类信息String type = child.getStringAttribute("type");try {// 转换成类信息Class<?> clazz = Resources.classForName(type);if (alias == null) {// 别名为null则通过类进行别名注册(如果存在注解则使用注解配置的别名,不存在则使用类简单名称TypeAliasRegistry类中有说明)typeAliasRegistry.registerAlias(clazz);} else {// 别名不为null则直接注册typeAliasRegistry.registerAlias(alias, clazz);}} catch (ClassNotFoundException e) {throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);}}}}}

  别名的注册已经了解了,mapper.xml配置的sql中使用的parameterType和resultType是如何解析的呢?mapper.xml中<select|insert|update|delete>最终都会转换成MappedStatement。MappedStatement是由XMLStatementBuilder的parseStatementNode方法进行解析的。
parseStatementNode代码如下:

// 解析<select|insert|update|delete>语句
public void parseStatementNode() {// context为解析当前语句的信息(select标签内包含的信息)。// 获取parameterType的值String parameterType = context.getStringAttribute("parameterType");// 根据parameterType的值通过TypeAliasRegistry获取对应的类。如何是null则直接返回null,如果是别名则根据别名获取类信息,不是别名则根据获取的值转换成对应的类。Class<?> parameterTypeClass = resolveClass(parameterType);// 获取resultType的值String resultType = context.getStringAttribute("resultType");// 与parameterType相同Class<?> resultTypeClass = resolveClass(resultType);
}

总结

  mybatis别名是通过TypeAliasRegistry进行存储的,TypeAliasRegistry通过内部的Map存储别名与类的关系。创建Configuration时会创建TypeAliasRegistry对象,并初始化默认的别名信息,解析Configuration的过程中会对自定的别名进行解析。自定义别名的方式分为两种,一种是通过package对包下的类定义别名,一种是根据typeAlias定义单个类。可以通过Alias注解定义单个类别名,使用注解前需要使用package或typeAlias,否则注解不生效。同一个类可以定义多个别名,不同的类不能定义相同的别名。

MyBatis-别名详细分析相关推荐

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

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

  2. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  3. Java-Mybatis(二): Mybatis配置解析、resultMap结果集映射、日志、分页、注解开发、Mybatis执行流程分析

    Java-Mybatis-02 学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1NE411Q7Nx 学习资料:mybatis 参考文档 – ht ...

  4. MyBatis 源码分析系列文章导读 1

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  5. MyBatis 源码分析 - 配置文件解析过程

    文章目录 * 本文速览 1.简介 2.配置文件解析过程分析 2.1 配置文件解析入口 2.2 解析 properties 配置 2.3 解析 settings 配置 2.3.1 settings 节点 ...

  6. mybatis 源码分析, 初始化

    分析版本 我们先分析xml配置文件形式的 mybatis, 等我们分析完毕了, 可以去分析 mybatis 和 spring 的对接 pom.xml <dependency><gro ...

  7. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  8. MyBatis 源码分析 - 内置数据源

    1.简介 本篇文章将向大家介绍 MyBatis 内置数据源的实现逻辑.搞懂这些数据源的实现,可使大家对数据源有更深入的认识.同时在配置这些数据源时,也会更清楚每种属性的意义和用途.因此,如果大家想知其 ...

  9. MyBatis 源码分析 - SQL 的执行过程

    本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...

最新文章

  1. React Axios 请求解决跨域问题
  2. java中日期怎么比较_如何比较Java中的日期?
  3. 伯努利分布方差_统计知识(4)——分布
  4. 十大编程算法助程序员走上大神路
  5. clob和blob是不是可以进行模糊查询_为省几十元买假内存条?金士顿内存条真伪查询与辨别方法...
  6. Qt-线程启动与关闭实例
  7. 原版英文书籍《Linux命令行》阅读记录7 | 一些键盘按键技巧
  8. Vue——this.$nextTick()
  9. 小爱同学App下架苹果App Store 网友:SiriOS警告?
  10. python3携程多任务_python3之携程yield及greenlet
  11. 1.2.3休眠(Sleeping)
  12. 浏览器自动调html5,HTML与浏览器
  13. 项目经理价值的最终体现
  14. python 自动换ip_python实现自动更换ip的方法
  15. 桌面窗口管理器占用过高解决办法
  16. vscode 格式化后函数后空格被删
  17. 高斯-赛德尔(Gauss-Seidel)解线性方程组的Matlab实现
  18. ZjDroid--脱壳神器介绍
  19. HTML+CSS系列学习 第五篇
  20. c 语言字体怎么改,Notepad++设置字体语言格式方法介绍

热门文章

  1. 手机小技巧:小米手机怎么恢复删除的照片?
  2. 10- ESP8266自主WiFi配网
  3. 安卓手机分辨率、尺寸、像素如何影响布局表现?
  4. show-overflow-tooltip不生效
  5. 10本区块链热门图书(应用开发、智能合约等)免费送!
  6. 百度地图API - 企业位置定位
  7. CISA Cert Prep: 5 Information Asset Protection for IS Auditors CISA证书准备:5 IS审计员的信息资产保护 Lynda课程中文字幕
  8. Mysql灵魂总结,知识重点,入门到精通,全细节,一篇到运维!
  9. 站群系统 java_站群管理系统 java
  10. 11.17定时调度(血干JAVA系类)