Mybatis源码篇(三)—— settings、typeAliases标签解析

添加配置settings和typeAliases:

<configuration>

    <properties resource="db.properties">
        <property name="userName" value="root"/>
        <property name="password" value="123456"/>
    </properties>

    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <setting name="defaultStatementTimeout" value="25"/>
        <setting name="defaultFetchSize" value="100"/>
        <setting name="safeRowBoundsEnabled" value="false"/>
    </settings>

    <typeAliases>
        <typeAlias type="cn.zsm.mybatis.student.Student" alias="student"/>
        <package name="cn.zsm.mybatis.man"/>
    </typeAliases>

    <environments default="development">
        .......

注意,标签的顺序不能乱,否则会报:Error creating document instance.  Cause: org.xml.sax.SAXParseException;

启动测试类,查看标签解析过程:

解析settings标签和typeAliases标签的方法:

Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings); //解析并设置vfs实现
this.loadCustomLogImpl(settings);  //解析并设置日志实现
this.typeAliasesElement(root.evalNode("typeAliases"));
......
this.settingsElement(settings); //设置其他属性

settingsAsProperties:

    private Properties settingsAsProperties(XNode context) {
        if (context == null) {
            return new Properties();
        } else {
            //获取配置文件中setting配置
            Properties props = context.getChildrenAsProperties();
            //获取所有可以设置的属性信息
            MetaClass metaConfig = MetaClass.forClass(Configuration.class, this.localReflectorFactory);
            //迭代配置文件中的属性信息
            Iterator var4 = props.keySet().iterator();

            Object key;
            do {
                if (!var4.hasNext()) {
                    return props;
                }

                key = var4.next();
            } while(metaConfig.hasSetter(String.valueOf(key))); //如果配置信息是可配置的属性,则继续,否则抛异常

            throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
        }
    }

解析settings标签返回一个Properties类,这个类是HashTable的子类,也就是说Properties其实就是个Map集合:

public class Properties extends Hashtable<Object,Object>{......}

在settingsAsProperties方法中,另一个比较重要的类是MetaClass,用来存储mybatis内置的设置的属性信息,只有是这些属性时,才会解析存储,否则会抛出异常BuilderException。

MetaClass:

MetaClass中有两个比较重要的属性reflectorFactory和reflector

public class MetaClass {
    private final ReflectorFactory reflectorFactory;
    private final Reflector reflector;
    
    .......
}

ReflectorFactory:

public interface ReflectorFactory {
    boolean isClassCacheEnabled();

    void setClassCacheEnabled(boolean var1);

    Reflector findForClass(Class<?> var1);
}

ReflectorFactory,根据类名就可以判断出它是Reflrector工厂,又来生成Reflector实例。

Reflector:

public class Reflector {
    private final Class<?> type;
    private final String[] readablePropertyNames;
    private final String[] writeablePropertyNames;
    private final Map<String, Invoker> setMethods = new HashMap();
    private final Map<String, Invoker> getMethods = new HashMap();
    private final Map<String, Class<?>> setTypes = new HashMap();
    private final Map<String, Class<?>> getTypes = new HashMap();
    private Constructor<?> defaultConstructor;
    private Map<String, String> caseInsensitivePropertyMap = new HashMap();
    
    ......
}

Reflector中定义了很多的Map对象,用于存放各种方法和类信息,我们debug模式查看该类中都存放了哪些信息:

props中存放我们配置文件中settings标签下的信息:

metaConfig:

setMethods、getMethods、setTypes、getTypes这四个属性中存放了设置settings中可以设置的所有属性信息:

看看这里的信息和我们在settings中可以设置的信息是不是一样,如果不了解settings中可以设置的信息可以参考博文:Mybatis实践篇(四)中:Setting:mybatis行为设置。

typeAliasesElement:

    private void typeAliasesElement(XNode parent) {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(var2.hasNext()) {
                XNode child = (XNode)var2.next();
                String alias;
                //如果子节点是package, 那么就获取package节点的name属性, mybatis会扫描指定的package
                if ("package".equals(child.getName())) {
                    alias = child.getStringAttribute("name");
                    //TypeAliasRegistry 负责管理别名,通过TypeAliasRegistry 进行别名注册
                    this.configuration.getTypeAliasRegistry().registerAliases(alias);
                } else {
                    //如果子节点是typeAlias节点,那么就获取alias属性和type的属性值
                    alias = child.getStringAttribute("alias");
                    String type = child.getStringAttribute("type");

                    try {
                        Class<?> clazz = Resources.classForName(type);
                        if (alias == null) {
                            this.typeAliasRegistry.registerAlias(clazz);
                        } else {
                            this.typeAliasRegistry.registerAlias(alias, clazz);
                        }
                    } catch (ClassNotFoundException var7) {
                        throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + var7, var7);
                    }
                }
            }
        }
    }

typeAliasesElement的解析主要就是注册bean以及beanName,而通过上面的源码可以看出,mybatis注册bean是通过registerAliases方法实现的,线面我们就来看看registerAliases方法的源码。

TypeAliasRegistry :

public class TypeAliasRegistry {
    private final Map<String, Class<?>> TYPE_ALIASES = new HashMap();

    //mybatis默认设置的别名,这里省略了很多
    public TypeAliasRegistry() {
        this.registerAlias("string", String.class);
        this.registerAlias("byte", Byte.class); 
        ...... 
        this.registerAlias("arraylist", ArrayList.class); 
        this.registerAlias("iterator", Iterator.class);
        this.registerAlias("ResultSet", ResultSet.class);
    }

    //处理别名, 直接从保存有别名的hashMap中取出即可
    public <T> Class<T> resolveAlias(String string) {
        try {
            if (string == null) {
                return null;
            } else {
                String key = string.toLowerCase(Locale.ENGLISH);
                Class value;
                if (this.TYPE_ALIASES.containsKey(key)) {
                    value = (Class)this.TYPE_ALIASES.get(key);
                } else {
                    value = Resources.classForName(string);
                }
                return value;
            }
        } catch (ClassNotFoundException var4) {
            throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + var4, var4);
        }
    }
    
   /**
   * 配置文件中配置为package的时候, 会调用此方法,根据配置的报名去扫描javabean ,然后自动注册别名
   * 默认会使用 Bean 的首字母小写的非限定类名来作为它的别名
   * 也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user)
   */
    public void registerAliases(String packageName) {
        this.registerAliases(packageName, Object.class);
    }

    public void registerAliases(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
        resolverUtil.find(new IsA(superType), packageName);
        Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
        Iterator var5 = typeSet.iterator();

        while(var5.hasNext()) {
            Class<?> type = (Class)var5.next();
            if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
                this.registerAlias(type);
            }
        }
    }

    public void registerAlias(Class<?> type) {
        String alias = type.getSimpleName();
        Alias aliasAnnotation = (Alias)type.getAnnotation(Alias.class);
        if (aliasAnnotation != null) {
            alias = aliasAnnotation.value();
        }
        this.registerAlias(alias, type);
    }

    //这就是注册别名的本质方法, 其实就是向保存别名的hashMap新增值而已
    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.TYPE_ALIASES.containsKey(key) && this.TYPE_ALIASES.get(key) != null && !((Class)this.TYPE_ALIASES.get(key)).equals(value)) {
                throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.TYPE_ALIASES.get(key)).getName() + "'.");
            } else {
                this.TYPE_ALIASES.put(key, value);
            }
        }
    }

    public void registerAlias(String alias, String value) {
        try {
            this.registerAlias(alias, Resources.classForName(value));
        } catch (ClassNotFoundException var4) {
            throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + var4, var4);
        }
    }

    public Map<String, Class<?>> getTypeAliases() {
        return Collections.unmodifiableMap(this.TYPE_ALIASES);
    }

从上面的源码可以看出,设置别名的原理就就是将其存入到一个map中,Mybatis默认给我们设置了不少别名

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值