天天看點

Mybatis源碼篇(三)—— settings、typeAliases标簽解析添加配置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标簽下的資訊:

Mybatis源碼篇(三)—— settings、typeAliases标簽解析添加配置settings和typeAliases:啟動測試類,檢視标簽解析過程:

metaConfig:

Mybatis源碼篇(三)—— settings、typeAliases标簽解析添加配置settings和typeAliases:啟動測試類,檢視标簽解析過程:

setMethods、getMethods、setTypes、getTypes這四個屬性中存放了設定settings中可以設定的所有屬性資訊:

Mybatis源碼篇(三)—— settings、typeAliases标簽解析添加配置settings和typeAliases:啟動測試類,檢視标簽解析過程:

看看這裡的資訊和我們在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預設給我們設定了不少别名

繼續閱讀