Mybatis源碼閱讀(一):Mybatis初始化1.2 —— 解析別名、插件、對象工廠、反射工具箱、環境

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
SimpleExecutor
接上一節 上一節:解析properties和settings

解析typeAliases

typeAliases節點用于配置別名。別名在mapper中使用resultType時會使用到,是對實體類的簡寫。

別名有兩種配置方式

1.通過package,直接掃描指定包下所有的類,注冊別名
2.通過typeAliase,指定某個類為其注冊別名
別名注冊代碼如下

/*** 解析typeAliases節點** @param parent*/private void typeAliasesElement(XNode parent) {if (parent != null) {// 遍歷所有子節點// typeAliases節點有兩個子節點,分別是package和typeAliasfor (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {// 獲取name屬性,package的name屬性指定的是包名String typeAliasPackage = child.getStringAttribute("name");// 將這個包下的所有類注冊別名configuration.getTypeAliasRegistry().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) {throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);}}}}}

注冊別名

掃包后獲取到包下所有的類之后,會為這些類生成別名,并將其注冊到Configuration中

/*** 指定包名,將這個包下所有的類都注冊別名** @param packageName*/public void registerAliases(String packageName) {registerAliases(packageName, Object.class);}/*** 為指定包下所有的類注冊別名** @param packageName* @param superType*/public void registerAliases(String packageName, Class<?> superType) {ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();// 掃描指定包下所有繼承了superType的類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);}}}/*** 注冊指定類的別名* @param type*/public void registerAlias(Class<?> type) {// 得到類的簡寫名稱,即不帶包名的名稱// 因此在mybatis掃描包下,不允許有同樣類名的類存在// 否則在啟動時就會報錯String alias = type.getSimpleName();Alias aliasAnnotation = type.getAnnotation(Alias.class);if (aliasAnnotation != null) {// 如果有Alias注解,就以Alias注解指定的別名為準// 該注解可以用于解決被掃描包下含有相同名稱類的問題alias = aliasAnnotation.value();}registerAlias(alias, type);}/*** 注冊別名* @param alias 別名* @param value 指定的類*/public void registerAlias(String alias, Class<?> value) {if (alias == null) {throw new TypeException("The parameter alias cannot be null");}// 別名轉為小寫String key = alias.toLowerCase(Locale.ENGLISH);// 如果已經有了這個別名,并且這個別名中取到的值不為null,并且取到的值和傳進來的類不相同就報錯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中。key是別名,因此別名不可以重復typeAliases.put(key, value);}

在注冊別名時,會使用到ResolverUtil工具類。該工具類可以根據指定的條件去查找指定包下的類。該類有個內部接口Test,接口中只有一個matches方法,用于根據指定的規則去匹配。Test接口有兩個實現。ISA用于檢測該類是否繼承了指定的類或者接口,而AnnotatedWith則用于檢測是否添加了指定的注釋,代碼比較簡單這里就不貼了。這里使用到了find方法,用于匹配指定包下所有繼承了superType的類

public ResolverUtil<T> find(Test test, String packageName) {String path = getPackagePath(packageName);try {// 獲取指定包下所有的文件名List<String> children = VFS.getInstance().list(path);for (String child : children) {if (child.endsWith(".class")) {addIfMatching(test, child);}}} catch (IOException ioe) {log.error("Could not read package: " + packageName, ioe);}return this;}/*** 如果匹配成功,就添加到matches中** @param test the test used to determine if the class matches* @param fqn  the fully qualified name of a class*/@SuppressWarnings("unchecked")protected void addIfMatching(Test test, String fqn) {try {String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');ClassLoader loader = getClassLoader();if (log.isDebugEnabled()) {log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");}Class<?> type = loader.loadClass(externalName);if (test.matches(type)) {matches.add((Class<T>) type);}} catch (Throwable t) {log.warn("Could not examine class '" + fqn + "'" + " due to a " +t.getClass().getName() + " with message: " + t.getMessage());}}

解析plugins

mybatis擁有強大的插件機制,可以通過配置mybatis攔截器來統一對sql、參數、返回集等進行處理,該功能廣泛運用與分頁、創建人等字段賦值、邏輯刪除、樂觀鎖等插件的編寫中。mybatis的攔截器編寫難度比spring mvc高得多,想要熟練地編寫mybatis攔截器,需要對源碼比較熟悉。

解析攔截器的代碼比較簡單,plugin節點需要配置一個interceptor屬性,該屬性是自定義攔截器的全類名。在該方法中會先獲取到該屬性,通過該屬性對應攔截器的默認構造去創建實例,并添加到Configuration中。

 /*** 解析plugins節點* plugin節點用于配置插件* 即 mybatis攔截器** @param parent* @throws Exception*/private void pluginElement(XNode parent) throws Exception {if (parent != null) {// 獲取子節點,子節點就是所配置的攔截器for (XNode child : parent.getChildren()) {// 獲得攔截器全類名String interceptor = child.getStringAttribute("interceptor");// 將節點下的節點封裝成propertiesProperties properties = child.getChildrenAsProperties();// 根據攔截器的全類名,通過默認構造方法創建一個實例Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();// 將攔截器節點下的properties放到攔截器中interceptorInstance.setProperties(properties);// 將攔截器添加到配置中configuration.addInterceptor(interceptorInstance);}}}

解析ObjectFactory節點

objectFactory用來處理查詢得到的結果集,創建對象去將結果集封裝到對象中。

mybatis默認的對象工廠是用無參構造或者有參構造去創建對象,而如果開發者想在創建對象前對其進行一些初始化操作或者處理一些業務方面的邏輯,就可以自定義對象工廠并進行配置。對象工廠的解析比較簡單,拿到type屬性去創建一個實例并添加到Configuration即可。

/*** 解析objectFactory節點* objectFactory用來處理查詢得到的結果集* 創建對象去將結果集封裝到對象中* mybatis默認的object工廠是用無參構造或者有參構造去創建對象* 而如果開發者想在創建對象前對其中的一些屬性做初始化操作* 或者做一些業務方面的邏輯* 就可以自己去創建對象工廠并進行配置** @param context* @throws Exception*/private void objectFactoryElement(XNode context) throws Exception {if (context != null) {// 拿到objectFactory節點的type屬性,該屬性為對象工廠的全類名String type = context.getStringAttribute("type");// 拿到節點下所有的propertiesProperties properties = context.getChildrenAsProperties();// 根據type對應的類,通過默認構造去創建一個實例ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();// 將properties放入對象工廠factory.setProperties(properties);// 將對象工廠添加到配置中去configuration.setObjectFactory(factory);}}

解析objectWrapperFactory和reflectorFactory

這兩個節點的解析很簡單,這里只貼上代碼給讀者去閱讀,很容易就能理解。

/*** 解析objectWrapperFactory節點** @param context* @throws Exception*/private void objectWrapperFactoryElement(XNode context) throws Exception {if (context != null) {// 獲取到type屬性String type = context.getStringAttribute("type");// 根據type屬性對應的類去創建對象ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();// 將對象放到配置中configuration.setObjectWrapperFactory(factory);}}/*** 解析reflectorFactory節點。代碼比較簡單就不寫了。* 解析流程和objectWrapperFactory一毛一樣** @param context* @throws Exception*/private void reflectorFactoryElement(XNode context) throws Exception {if (context != null) {String type = context.getStringAttribute("type");ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();configuration.setReflectorFactory(factory);}}

處理settings節點

在前一篇文章,已經將解析settings節點的代碼講解完畢,該方法則是用來將解析后的settings節點中的配置,一一添加到Configuration。代碼簡單粗暴,就是一堆set

/*** 將settings節點的配置set到Configuration中** @param props*/private void settingsElement(Properties props) {configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));configuration.setLogPrefix(props.getProperty("logPrefix"));configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));}

解析environments

該節點標識環境配置。所謂環境,就是只運行時需要的一系列參數,也可以理解成開發中常說的“開發環境”“測試環境”“生產環境”。環境最直觀的就是在不同環境下連接不同的數據庫。

environments節點下提供了數據源和事務配置。

/*** 解析environments節點* 該節點表示環境配置* 所謂環境,就是指運行時環境,即開發環境、測試環境、生產環境* 環境最直觀的提現就是在不同環境下數據庫不同* environments節點下就提供了數據源和事務配置** @param context* @throws Exception*/private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {// 獲取默認的環境idenvironment = context.getStringAttribute("default");}for (XNode child : context.getChildren()) {// 拿到子節點的id。父節點的default屬性對應的就是子節點idString id = child.getStringAttribute("id");if (isSpecifiedEnvironment(id)) {// 解析事務管理器TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));// 解析數據工廠DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));// 從工廠中拿到數據庫DataSource dataSource = dsFactory.getDataSource();// 創建環境并set到Configuration中去Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}

解析的邏輯中,需要一并解析事務工廠和DataSource工廠。代碼比較簡單,和objectWrapperFactory一樣,這里就不貼了

解析databaseIdProvider

該節點用于提供多數據源支持。這里的多數據源并非指多個數據庫,而是指多個數據庫產品。這里的代碼和objectWrapperFactory比較類似,不做過多解釋。

 /*** 解析databaseIdProvider節點* 該節點用于提供多數據源支持* 這里的多數據源并非指多個數據庫* 而是指多個數據庫產品* 這里的代碼和objectFactory類似** @param context* @throws Exception*/private void databaseIdProviderElement(XNode context) throws Exception {DatabaseIdProvider databaseIdProvider = null;if (context != null) {String type = context.getStringAttribute("type");// awful patch to keep backward compatibilityif ("VENDOR".equals(type)) {type = "DB_VENDOR";}Properties properties = context.getChildrenAsProperties();databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();databaseIdProvider.setProperties(properties);}// 獲取當前環境Environment environment = configuration.getEnvironment();if (environment != null && databaseIdProvider != null) {String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());configuration.setDatabaseId(databaseId);}}

結語

昨天加班填坑加到了12點,就沒有繼續為代碼加注釋,今天在空閑的時候就繼續填坑了。目前對mybatis-config.xml文件的解析基本接近尾聲,還差typeHandlers和mappers兩個節點沒有進行注釋。相信看了這兩篇文章的讀者對于解析配置文件的邏輯已經有了一定的理解,因此自己閱讀后面兩個節點的代碼解析應該不難。明天或者后天會將最后的兩個節點的解析補上

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
SimpleExecutor

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536954.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536954.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536954.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

中西方對時間的差異_中西方時間觀念差異 英文

The concept of time(時間觀念)①Inchina&#xff0c;words and phrases about time are very general. Forexample&#xff0c;ifyoudatewithsomeone,mostofChineseusedtoanswer: in the afternoon /at night/after a while and so on.Butinwestern,peoplehaveaverystrongconc…

Google 修改 Chrome API,防止隱身模式檢測

開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f; 在使用 Chrome 瀏覽網頁時&#xff0c;某些網站會使用某種方法來確定訪問者是否處于隱身模式&#xff0c;這是一種隱私泄漏行為。Google 目前正在考慮修改 Chrome 的相關 API&#xff0c;來杜絕…

Mybatis源碼閱讀(一):Mybatis初始化1.1 解析properties、settings

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

亞馬遜推薦python_使用python查找amazon類別

我想得到amazon的類別&#xff0c;我計劃廢棄不用API。我已經取消了http://www.amazon.com&#xff0c;我已經在Shop By Department下拉列表中抓取了所有的類別和子類別&#xff0c;我創建了一個web服務來完成這項工作&#xff0c;代碼就在這里route(/hello)def hello():textli…

JavaScript異步基礎

唯一比不知道代碼為什么崩潰更可怕的事情是&#xff0c;不知道為什么一開始它是工作的&#xff01;在 ECMA 規范的最近幾次版本里不斷有新成員加入&#xff0c;尤其在處理異步的問題上&#xff0c;更是不斷推陳出新。然而&#xff0c;我們在享受便利的同時&#xff0c;也應該了…

Flutter、ReactNative、uniapp對比

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

JavaScript數組方法

一、基本類型和引用類型 數值、字符串、布爾值、undefined、null可以直接寫出來&#xff0c;比較簡單的數據稱為基本類型&#xff0c;在比較的時候&#xff0c;是直接按值比較。對象、函數、數組復雜的數據是引用類型&#xff0c;在比較的時候&#xff0c;是按照地址比較。cons…

nodejs mysql模塊_NodeJs使用Mysql模塊實現事務處理

依賴模塊&#xff1a;1. mysql&#xff1a;https://github.com/felixge/node-mysqlnpm install mysql --save2. async&#xff1a;https://github.com/caolan/asyncnpm install async --save(ps: async模塊可換成其它Promise模塊如bluebird、q等)因為Node.js的mysql模塊本身對于…

計數排序vs基數排序vs桶排序

從計數排序說起 計數排序是一種非基于元素比較的排序算法&#xff0c;而是將待排序數組元素轉化為計數數組的索引值&#xff0c;從而間接使待排序數組具有順序性。 計數排序的實現一般有兩種形式&#xff1a;基于輔助數組和基于桶排序。 基于輔助數組 整個過程包含三個數組&…

多線程中ThreadLocal的使用

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

mysql 查看所有表的引擎_MySQL查看數據庫、表的占用空間大小以及某個庫中所有表的引擎類型...

本文章來給大家介紹一些常用的MySQL查看數據庫、表的占用空間大小sql命令吧&#xff0c;希望此教程 對各位同學會有所幫助。查看各庫的大小代碼如下復制代碼SELECT SUM(DATA_LENGTH)SUM(INDEX_LENGTH) FROM information_schema.tables WHERE TABLE_SCHEMAdatabase_name;結果是以…

Fusion組件庫是如何支持多語言能力的

隨著國際化發展&#xff0c;多語言的需求越來越常見&#xff0c;單一的語言已經遠不能滿足需求了。作為一個組件庫&#xff0c;支持多語言也是基本能力。 多語言功能的本質其實是文本的替換&#xff0c;一個詞匯“OK”&#xff0c;在英文語境下是“OK”&#xff0c;日語語境下是…

mysql 存儲過程 replace_mysql replace存儲過程

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云數據庫專家保駕護航&#xff0c;為用戶…

注解版poi操作工具

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Kali Linux 2019.1 發布,Metasploit 更新到 5.0 版本

百度智能云 云生態狂歡季 熱門云產品1折起>>> Kali Linux 2019.1 發布了&#xff0c;Kali 前身 BackTrack&#xff0c;它是一個基于 Debian 的 Linux 發行版&#xff0c;主要用于信息安全行業&#xff0c;其包含了一系列安全、滲透測試和取證工具。此版本 Linux 內核…

peewee mysql_scrapy中利用peewee插入Mysql

前兩天老大布置一個任務&#xff0c;說爬下來的數據要存入數據庫中&#xff0c;丟給我一個peewee&#xff0c;說用這個。當時的我兩眼一抹黑&#xff0c;這是個什么東西呀&#xff0c;我知道scrapy的數據存入數據庫是在pipelines中進行設置但是peewee是什么東西呢。經過兩天不懈…

Java版數據結構與算法——線性表

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

基于 CODING 的 Spring Boot 持續集成項目

本文作者&#xff1a;CODING 用戶 - 廖石榮 持續集成的概念 持續集成(Continuous integration,簡稱 CI&#xff09;是一種軟件開發實踐&#xff0c;即團隊開發成員經常集成他們的工作&#xff0c;通常每個成員每天至少集成一次&#xff0c;也就意味著每天可能會發生多次集成。每…

lvs mysql 端口_LVS配置及多端口服務配置

一、5、各主機IP地址&#xff1a;主機IP網關Client192.168.86.116RouterF0/0:192.168.x.xFo/1:192.168.xx.xxF0/1DirectorEth0:192.168.86.111/24(DIP)Eth0:1:192.168.86.254/32(VIP)F0/1Real 1Eth0:192.168.86.112/24(DIP)lo:1:192.168.86.254/32(VIP)F0/1Real 2Eth0:192.168.…

Mybatis組成部分

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…