暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MyBatis 源码解读(五)类型别名

Kenny纪实号 2021-03-17
211

typeAliasesElement 加载类型别名

解析配置的别名
XMLConfigBuilder.typeAliasesElement(root.evalNode(“typeAliases”));

创建 User
 类,用于辅助测试

 1package com.tky.ibatis.model;
2// ......
3public class User implements Serializable{
4
5    private Long id ;
6    private Long version ;
7    private String username ;
8    private String password ;
9    private Date createTime ;
10    // setter and getter
11}

首先在 mybatis-config.xml
 configuration
 节点中加入如下配置

1<typeAliases>
2  <typeAlias type="com.tky.ibatis.model.User" alias="typeAlias_user"/>
3  <package name="com.tky.ibatis.model"/>
4</typeAliases>

  1. 类型别名可为 Java 类型设置一个缩写名字。它仅用于 XML 配置,意在降低冗余的全限定类名书写。

  2. typeAliases
     节点允许添加两种节点(typeAlias,package
    ),如果同时使用两种方式要求 typeAlias
     的声明必须在 package
    之前

  3. 它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。例如:long
     -> _long

处理逻辑代码

 1    private void typeAliasesElement(XNode parent) {
2        if (parent != null) {
3            // 获取 typeAliases 节点下的子节点进行处理
4            for (XNode child : parent.getChildren()) {
5                // 使用package元素指定 将包下的类型全部映射对应的别名简称
6                if ("package".equals(child.getName())) {
7                    String typeAliasPackage = child.getStringAttribute("name");
8                    typeAliasRegistry.registerAliases(typeAliasPackage);
9                } else { // 使用typeAlias元素直接指定
10                    String alias = child.getStringAttribute("alias");
11                    String type = child.getStringAttribute("type");
12                    try {
13                        Class<?> clazz = Resources.classForName(type);
14                        if (alias == null) {
15                            typeAliasRegistry.registerAlias(clazz);
16                        } else {
17                            typeAliasRegistry.registerAlias(alias, clazz);
18                        }
19                    } catch (ClassNotFoundException e) {}
20                }
21            }
22        }
23    }

package
 最终解析使用的是 TypeAliasRegistry.registerAliases(String packageName)
方法进行解析,通过ResolverUtil
 工具找到指定包路径下的所有class
 文件然后注册进系统。

其中匿名内部类、接口和局部成员类是不会被注册的,这些类通常只进行一次使用或者没有具体实现,不适合作为常量注册进系统使用。

typeAlias
则直接使用 TypeAliasRegistry.registerAlias(String alias, String value)
进行注册。

1. 使用`typeAlias`元素直接指定
1public void registerAlias(String alias, Class<?> value) {
2    String key = alias.toLowerCase(Locale.ENGLISH);
3    // 对于已经注册过的值不进行覆盖处理
4    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
5        throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
6    }
7    typeAliases.put(key, value);
8}

2. 使用`package`元素 注册包下所有类型

通过工具类 ResolverUtil
 找到该包下的所有的类,然后使用类的名称(SimpleName
)作为别名将类型注册进系统。

 1public void registerAliases(String packageName) {
2  registerAliases(packageName, Object.class);
3}
4
5public void registerAliases(String packageName, Class<?> superType) {
6  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
7  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
8  Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
9  for (Class<?> type : typeSet) {
10    if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
11      registerAlias(type);
12    }
13  }
14}
15
16public void registerAlias(Class<?> type) {
17  // 不指定别名时,默认使用类名作为别名
18  String alias = type.getSimpleName();
19  Alias aliasAnnotation = type.getAnnotation(Alias.class);
20  if (aliasAnnotation != null) {
21    alias = aliasAnnotation.value();
22  }
23  registerAlias(alias, type);
24}

`ResolverUtil` 辅助类处理工具

这里有一个有趣的工具类 ResolverUtil

ResolverUtil.find(new ResolverUtil.IsA(superType), packageName)
 查找指定包路径下的类,两个参数:第一个指定过滤器,指定要过滤的超类;第二个指明包路径,指明要查找那个包下面的类。

实现代码如下,VFS.getInstance().list(path)
 就是MyBatis 源码解读(三)指定VFS 的实现 介绍的配置

 1  public ResolverUtil<T> find(Test test, String packageName) {
2    String path = packageName == null ? null : packageName.replace('.''/');
3    try {
4      // 可自定义VFS 实例
5      List<String> children = VFS.getInstance().list(path);
6      for (String child : children) {
7        if (child.endsWith(".class")) {
8          // 将找到的类型添加进集合用于后续处理
9          addIfMatching(test, child);
10        }
11      }
12    } catch (IOException ioe) {}
13    return this;
14  }
15
16  // 添加进集合用于后续处理
17  protected void addIfMatching(Test test, String fqn) {
18    try {
19        String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/''.');
20        ClassLoader loader = getClassLoader();
21        Class<?> type = loader.loadClass(externalName);
22        if (test.matches(type)) {
23            matches.add((Class<T>) type);
24        }
25    } catch (Throwable t) {}
26}

编写单元测试,看看运行结果是否和我们预期的一致。

 1public class TypeAliasesTest extends BaseTest {
2    @Override
3    public String getResource() {
4        return "com/tky/perperties/mybatis-config.xml";
5    }
6    @Test
7    public void testAliases() {
8        Map<String, Class<?>> types = sqlSessionFactory.getConfiguration().getTypeAliasRegistry().getTypeAliases();
9        assertEquals(User.class, types.get("typealias_user"));
10        assertEquals(User.class, types.get("user"));
11    }
12}


文章转载自Kenny纪实号,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论