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

typeAliasesElement 加载类型别名

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

创建 User 类,用于辅助测试

package com.tky.ibatis.model;
// ......
public class User implements Serializable{

    private Long id ;
    private Long version ;
    private String username ;
    private String password ;
    private Date createTime ;
    // setter and getter
}

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

<typeAliases>
  <typeAlias type="com.tky.ibatis.model.User" alias="typeAlias_user"/>
  <package name="com.tky.ibatis.model"/>
</typeAliases>
  1. 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
  2. typeAliases 节点允许添加两种节点(typeAlias,package),如果同时使用两种方式要求 typeAlias 的声明必须在 package之前
  3. 它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。例如:long -> _long

处理逻辑代码

	private void typeAliasesElement(XNode parent) {
		if (parent != null) {
			// 获取 typeAliases 节点下的子节点进行处理
			for (XNode child : parent.getChildren()) {
				// 使用package元素指定 将包下的类型全部映射对应的别名简称
				if ("package".equals(child.getName())) {
					String typeAliasPackage = child.getStringAttribute("name");
					typeAliasRegistry.registerAliases(typeAliasPackage);
				} else { // 使用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) {}
				}
			}
		}
	}

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

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

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

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

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

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

public void registerAliases(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  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()) {
      registerAlias(type);
    }
  }
}

public void registerAlias(Class<?> type) {
  // 不指定别名时,默认使用类名作为别名
  String alias = type.getSimpleName();
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  }
  registerAlias(alias, type);
}
ResolverUtil 辅助类处理工具

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

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

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

  public ResolverUtil<T> find(Test test, String packageName) {
    String path = packageName == null ? null : packageName.replace('.', '/');
    try {
      // 可自定义VFS 实例
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          // 将找到的类型添加进集合用于后续处理
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {}
    return this;
  }
  
  // 添加进集合用于后续处理
  protected void addIfMatching(Test test, String fqn) {
	try {
		String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
		ClassLoader loader = getClassLoader();
		Class<?> type = loader.loadClass(externalName);
		if (test.matches(type)) {
			matches.add((Class<T>) type);
		}
	} catch (Throwable t) {}
}

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

public class TypeAliasesTest extends BaseTest {
	@Override
	public String getResource() {
		return "com/tky/perperties/mybatis-config.xml";
	}
	@Test
	public void testAliases() {
		Map<String, Class<?>> types = sqlSessionFactory.getConfiguration().getTypeAliasRegistry().getTypeAliases();
		assertEquals(User.class, types.get("typealias_user"));
		assertEquals(User.class, types.get("user"));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值