源码追踪,记typeAliasesPackage的使用(ruoyi-cloud中一个疑问的启发)

源码追踪,记typeAliasesPackage的使用(ruoyi-cloud中一个疑问的启发),第1张

源码追踪,记typeAliasesPackage的使用(ruoyi-cloud中一个疑问的启发)

首先,提一个思考题:在mapper.xml文件中写sql的时候,parameterType指明入参类型的时候,为什么只需要写String,Long,Integer等,而不用写java.lang.String,java.lang.Long这样呢?

其实是因为springboot在启动并自动初始化bean的过程中,会去加载一个叫TypeAliasRegistry的类,该类的构造器初始化了常用的所有数据类型如下:

private final Map> typeAliases = new HashMap();
public TypeAliasRegistry() {
        this.registerAlias("string", String.class);
        this.registerAlias("byte", Byte.class);
        this.registerAlias("long", Long.class);
        this.registerAlias("short", Short.class);
        this.registerAlias("int", Integer.class);
        this.registerAlias("integer", Integer.class);
        this.registerAlias("double", Double.class);
        this.registerAlias("float", Float.class);
        this.registerAlias("boolean", Boolean.class);
        this.registerAlias("byte[]", Byte[].class);
        this.registerAlias("long[]", Long[].class);
        this.registerAlias("short[]", Short[].class);
        this.registerAlias("int[]", Integer[].class);
        this.registerAlias("integer[]", Integer[].class);
        this.registerAlias("double[]", Double[].class);
        this.registerAlias("float[]", Float[].class);
        this.registerAlias("boolean[]", Boolean[].class);
        this.registerAlias("_byte", Byte.TYPE);
        this.registerAlias("_long", Long.TYPE);
        this.registerAlias("_short", Short.TYPE);
        this.registerAlias("_int", Integer.TYPE);
        this.registerAlias("_integer", Integer.TYPE);
        this.registerAlias("_double", Double.TYPE);
        this.registerAlias("_float", Float.TYPE);
        this.registerAlias("_boolean", Boolean.TYPE);
        this.registerAlias("_byte[]", byte[].class);
        this.registerAlias("_long[]", long[].class);
        this.registerAlias("_short[]", short[].class);
        this.registerAlias("_int[]", int[].class);
        this.registerAlias("_integer[]", int[].class);
        this.registerAlias("_double[]", double[].class);
        this.registerAlias("_float[]", float[].class);
        this.registerAlias("_boolean[]", boolean[].class);
        this.registerAlias("date", Date.class);
        this.registerAlias("decimal", BigDecimal.class);
        this.registerAlias("bigdecimal", BigDecimal.class);
        this.registerAlias("biginteger", BigInteger.class);
        this.registerAlias("object", Object.class);
        this.registerAlias("date[]", Date[].class);
        this.registerAlias("decimal[]", BigDecimal[].class);
        this.registerAlias("bigdecimal[]", BigDecimal[].class);
        this.registerAlias("biginteger[]", BigInteger[].class);
        this.registerAlias("object[]", Object[].class);
        this.registerAlias("map", Map.class);
        this.registerAlias("hashmap", HashMap.class);
        this.registerAlias("list", List.class);
        this.registerAlias("arraylist", ArrayList.class);
        this.registerAlias("collection", Collection.class);
        this.registerAlias("iterator", Iterator.class);
        this.registerAlias("ResultSet", ResultSet.class);
    }
public void registerAlias(String alias, Class value) {
        if (alias == null) {
            throw new TypeException("The parameter alias cannot be null");
        } else {
            String key = alias.toLowerCase(Locale.ENGLISH);
            if (this.typeAliases.containsKey(key) && this.typeAliases.get(key) != null && !((Class)this.typeAliases.get(key)).equals(value)) {
                throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.typeAliases.get(key)).getName() + "'.");
            } else {
                this.typeAliases.put(key, value);
            }
        }
    }

DEBUG启动过程中打上断点,效果如下,可以推测,当该类初始化完成之后,会将构造器中传入的所有数据类型装载到这个叫typeAliases的Map中。

 然后初始化sqlSessionFactory的过程中,会调用resolveAlias方法解析,代码如下

public  Class resolveAlias(String string) {
        try {
            if (string == null) {
                return null;
            } else {
                String key = string.toLowerCase(Locale.ENGLISH);
                Class value;
                if (this.typeAliases.containsKey(key)) {
                    value = (Class)this.typeAliases.get(key);
                } else {
                    value = Resources.classForName(string);
                }

                return value;
            }
        } catch (ClassNotFoundException var4) {
            throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + var4, var4);
        }
    }

DEBUG运行如下:

可以看到,我们在xml文件中写的string,在这里会先进if判断在typeAliases中是否有string,有就会进入下面的代码

value = (Class)this.typeAliases.get(key);

直接从里面取出string对应的全限定名java.lang.String。根据代码看,如果我们在xml中写全限定名java.lang.String,也是可以的,此时会进入else分支,通过反射获取到类。

到这里,前面的思考题就可以破案了。

为什么会去追这样一个属性的源码呢?源于开源项目ruoyi-cloud的system模块中的一个写法,在nacos配置中心可以看到,该模块采用了typeAliasesPackage为所有类指定了别名

 这里只写到了system,而实际上该模块的实体类都在com.ruoyi.system.domain下面,这个属性会扫描该目录以及其子目录,所以这样写是可以解析到的,没有问题。但是当我把domain加上去的时候,按道理说也应该不会有问题啊,然而启动的时候直接报错了,报一个找不到SysDept类的异常,于是debug了一下,效果如下:

可以看出,该类并不是 com.ruoyi.system.domain下面的,而是com.ruoyi.system.api.domain下面的,那么报错就是理所当然了。

找出错误后,就想着去跟一下这个别名的解析办法,于是有了这篇文章。

 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5695248.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存