本篇文章為大家展示了怎么從 Spring及Mybatis框架源碼中學(xué)習(xí)設(shè)計(jì)模式,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站建設(shè)、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的保山網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
設(shè)計(jì)模式是解決問(wèn)題的方案,從大神的代碼中學(xué)習(xí)對(duì)設(shè)計(jì)模式的使用,可以有效提升個(gè)人編碼及設(shè)計(jì)代碼的能力。本系列博文用于總結(jié)閱讀過(guò)的框架源碼(Spring 系列、Mybatis)及 JDK 源碼中 所使用過(guò)的設(shè)計(jì)模式,并結(jié)合個(gè)人工作經(jīng)驗(yàn),重新理解設(shè)計(jì)模式。
主要看一下創(chuàng)建型的幾個(gè)設(shè)計(jì)模式,即:單例模式、各種工廠模式及建造者模式。
確保某個(gè)類只有一個(gè)實(shí)例,并提供該實(shí)例的獲取方法。實(shí)際應(yīng)用很多,不管是框架、JDK 還是實(shí)際的項(xiàng)目開(kāi)發(fā),但大都會(huì)使用“餓漢式”或“枚舉”來(lái)實(shí)現(xiàn)單例?!皯袧h式”也有一些應(yīng)用,但通過(guò)“雙檢鎖機(jī)制”來(lái)保證單例的實(shí)現(xiàn)很少見(jiàn)。
最簡(jiǎn)單的就是 使用一個(gè)私有構(gòu)造函數(shù)、一個(gè)私有靜態(tài)變量,以及一個(gè)公共靜態(tài)方法的方式來(lái)實(shí)現(xiàn)。懶漢式、餓漢式等簡(jiǎn)單實(shí)現(xiàn)就不多 BB 咯,這里強(qiáng)調(diào)一下雙檢鎖懶漢式實(shí)現(xiàn)的坑,以及枚舉方式的實(shí)現(xiàn)吧,最后再結(jié)合 spring 源碼 擴(kuò)展一下單例 bean 的實(shí)現(xiàn)原理。
1. 雙檢鎖實(shí)現(xiàn)的坑
/*** @author 云之君* 雙檢鎖 懶漢式,實(shí)現(xiàn)線程安全的單例* 關(guān)鍵詞:JVM指令重排、volatile、反射攻擊*/public class Singleton3 { /** * 這里加個(gè)volatile進(jìn)行修飾,也是本單例模式的精髓所在。 * 下面的 instance = new Singleton3(); 這行代碼在JVM中其實(shí)是分三步執(zhí)行的: * 1、分配內(nèi)存空間; * 2、初始化對(duì)象; * 3、將instance指向分配的內(nèi)存地址。 * 但JVM具有指令重排的特性,實(shí)際的執(zhí)行順序可能會(huì)是1、3、2,導(dǎo)致多線程情況下出問(wèn)題, * 使用volatile修飾instance變量 可以 避免上述的指令重排 * tips:不太理解的是 第一個(gè)線程在執(zhí)行第2步之前就已經(jīng)釋放了鎖嗎?導(dǎo)致其它線程進(jìn)入synchronized代碼塊 * 執(zhí)行 instance == null 的判斷? * 回答:第一個(gè)線程在執(zhí)行第2步之前就已經(jīng)釋放了鎖嗎?(沒(méi)有)。如果不使用volatile修飾instance變量,那么其他線程進(jìn)來(lái)的時(shí)候,看到的instance就有可能不是null的,因?yàn)橐呀?jīng)執(zhí)行了第3步,那么此時(shí)這個(gè)線程(執(zhí)行 return instance;)使用的instance是一個(gè)沒(méi)有初始化的instance,就會(huì)有問(wèn)題。 */ private volatile static Singleton3 instance; private Singleton3(){ } public static Singleton3 getInstance(){ if(instance == null){ synchronized(Singleton3.class){ if(instance == null){ instance = new Singleton3(); } } } return instance; }}
2. 枚舉實(shí)現(xiàn)
其它的單例模式實(shí)現(xiàn)往往都會(huì)面臨序列化 和 反射攻擊的問(wèn)題,比如上面的 Singleton3 如果實(shí)現(xiàn)了 Serializable 接口,那么在每次序列化時(shí)都會(huì)創(chuàng)建一個(gè)新對(duì)象,若要保證單例,必須聲明所有字段都是 transient 的,并且提供一個(gè) readResolve()方法。反射攻擊可以通過(guò) setAccessible()方法將私有的構(gòu)造方法公共化,進(jìn)而實(shí)例化。若要防止這種攻擊,就需要在構(gòu)造方法中添加 防止實(shí)例化第二個(gè)對(duì)象的代碼。
枚舉實(shí)現(xiàn)的單例在面對(duì) 復(fù)雜的序列化及反射攻擊時(shí),依然能夠保持自己的單例狀態(tài),所以被認(rèn)為是單例的最佳實(shí)踐。比如,mybatis 在定義 SQL 命令類型時(shí)就使用到了枚舉。
package org.apache.ibatis.mapping;/** * @author Clinton Begin */public enum SqlCommandType { UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;}
1. java.lang.Runtime
/** * 每個(gè)Java應(yīng)用程序都有一個(gè)單例的Runtime對(duì)象,通過(guò)getRuntime()方法獲得 * @author unascribed * @see java.lang.Runtime#getRuntime() * @since JDK1.0 */public class Runtime { /** 很明顯,這里用的是餓漢式 實(shí)現(xiàn)單例 */ private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime() { return currentRuntime; } /** Don't let anyone else instantiate this class */ private Runtime() {}}
2. java.awt.Desktop
public class Desktop { /** * Suppresses default constructor for noninstantiability. */ private Desktop() { peer = Toolkit.getDefaultToolkit().createDesktopPeer(this); } /** * 由于對(duì)象較大,這里使用了懶漢式延遲加載,方式比較簡(jiǎn)單,直接把鎖加在方法上。 * 使用雙檢鎖方式實(shí)現(xiàn)的單例 還沒(méi)怎么碰到過(guò),有經(jīng)驗(yàn)的小伙伴 歡迎留言補(bǔ)充 */ public static synchronized Desktop getDesktop(){ if (GraphicsEnvironment.isHeadless()) throw new HeadlessException(); if (!Desktop.isDesktopSupported()) { throw new UnsupportedOperationException("Desktop API is not " + "supported on the current platform"); } sun.awt.AppContext context = sun.awt.AppContext.getAppContext(); Desktop desktop = (Desktop)context.get(Desktop.class); if (desktop == null) { desktop = new Desktop(); context.put(Desktop.class, desktop); } return desktop; }}
Spring 實(shí)現(xiàn)單例 bean 是使用 map 注冊(cè)表和 synchronized 同步機(jī)制實(shí)現(xiàn)的,通過(guò)分析 spring 的 AbstractBeanFactory 中的 doGetBean 方法和 DefaultSingletonBeanRegistry 的 getSingleton()方法,可以理解其實(shí)現(xiàn)原理。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { ...... /** * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * 真正實(shí)現(xiàn)向IOC容器獲取Bean的功能,也是觸發(fā)依賴注入(DI)功能的地方 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @SuppressWarnings("unchecked") protectedT doGetBean(final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { ...... //創(chuàng)建單例模式bean的實(shí)例對(duì)象 if (mbd.isSingleton()) { //這里使用了一個(gè)匿名內(nèi)部類,創(chuàng)建Bean實(shí)例對(duì)象,并且注冊(cè)給所依賴的對(duì)象 sharedInstance = getSingleton(beanName, new ObjectFactory
把同一系列類的實(shí)例化交由一個(gè)工廠類進(jìn)行集中管控。與其說(shuō)它是一種設(shè)計(jì)模式,倒不如把它看成一種編程習(xí)慣,因?yàn)樗环稀伴_(kāi)閉原則”,增加新的產(chǎn)品類需要修改工廠類的代碼。
public interface Hero { void speak();}public class DaJi implements Hero { @Override public void speak() { System.out.println("妲己,陪你玩 ~"); }}public class LiBai implements Hero{ @Override public void speak() { System.out.println("今朝有酒 今朝醉 ~"); }}/** 對(duì)各種英雄進(jìn)行集中管理 */public class HeroFactory { public static Hero getShibing(String name){ if("LiBai".equals(name)) return new LiBai(); else if("DaJi".equals(name)) return new DaJi(); else return null; }}
這種設(shè)計(jì)方式只在我們產(chǎn)品的“FBM 資金管理”模塊有看到過(guò),其中對(duì) 100+個(gè)按鈕類進(jìn)行了集中管控,不過(guò)其設(shè)計(jì)結(jié)構(gòu)比上面這種要復(fù)雜的多。
在頂級(jí)工廠(接口/抽象類)中定義 產(chǎn)品類的獲取方法,由具體的子工廠實(shí)例化對(duì)應(yīng)的產(chǎn)品,一般是一個(gè)子工廠對(duì)應(yīng)一個(gè)特定的產(chǎn)品,實(shí)現(xiàn)對(duì)產(chǎn)品的集中管控,并且符合“開(kāi)閉原則”。
mybatis 中數(shù)據(jù)源 DataSource 的獲取使用到了該設(shè)計(jì)模式。接口 DataSourceFactory 定義了獲取 DataSource 對(duì)象的方法,各實(shí)現(xiàn)類 完成了獲取對(duì)應(yīng)類型的 DataSource 對(duì)象的實(shí)現(xiàn)。(mybatis 的源碼都是縮進(jìn)兩個(gè)空格,難道國(guó)外的編碼規(guī)范有獨(dú)門派系?)
public interface DataSourceFactory { // 設(shè)置DataSource的屬性,一般緊跟在DataSource初始化之后 void setProperties(Properties props); // 獲取DataSource對(duì)象 DataSource getDataSource();}public class JndiDataSourceFactory implements DataSourceFactory { private DataSource dataSource; @Override public DataSource getDataSource() { return dataSource; } @Override public void setProperties(Properties properties) { try { InitialContext initCtx; Properties env = getEnvProperties(properties); if (env == null) { initCtx = new InitialContext(); } else { initCtx = new InitialContext(env); } if (properties.containsKey(INITIAL_CONTEXT) && properties.containsKey(DATA_SOURCE)) { Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT)); dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE)); } else if (properties.containsKey(DATA_SOURCE)) { dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE)); } } catch (NamingException e) { throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e); } }}public class UnpooledDataSourceFactory implements DataSourceFactory { protected DataSource dataSource; // 在實(shí)例化該工廠時(shí),就完成了DataSource的實(shí)例化 public UnpooledDataSourceFactory() { this.dataSource = new UnpooledDataSource(); } @Override public DataSource getDataSource() { return dataSource; }}public class PooledDataSourceFactory extends UnpooledDataSourceFactory { // 與UnpooledDataSourceFactory的不同之處是,其初始化的DataSource為PooledDataSource public PooledDataSourceFactory() { this.dataSource = new PooledDataSource(); }}public interface DataSource extends CommonDataSource, Wrapper { Connection getConnection() throws SQLException; Connection getConnection(String username, String password) throws SQLException;}
DataSource 最主要的幾個(gè)實(shí)現(xiàn)類內(nèi)容都比較多,代碼就不貼出來(lái)咯,感興趣的同學(xué)可以到我的源碼分析專題中看到詳細(xì)解析。
tips:什么時(shí)候該用簡(jiǎn)單工廠模式?什么時(shí)候該用工廠方法模式呢?
個(gè)人認(rèn)為,工廠方法模式符合“開(kāi)閉原則”,增加新的產(chǎn)品類不用修改代碼,應(yīng)當(dāng)優(yōu)先考慮使用這種模式。如果產(chǎn)品類結(jié)構(gòu)簡(jiǎn)單且數(shù)量龐大時(shí),還是使用簡(jiǎn)單工廠模式更容易維護(hù)些,如:上百個(gè)按鈕類。
設(shè)計(jì)結(jié)構(gòu)上與“工廠方法”模式很像,最主要的區(qū)別是,工廠方法模式中 一個(gè)子工廠只對(duì)應(yīng)一個(gè)具體的產(chǎn)品,而抽象工廠模式中,一個(gè)子工廠對(duì)應(yīng)一組具有相關(guān)性的產(chǎn)品,即,存在多個(gè)獲取不同產(chǎn)品的方法。這種設(shè)計(jì)模式也很少見(jiàn)人用,倒是“工廠方法”模式見(jiàn)的最多。
public abstract class AbstractFactory { abstract protected AbstractProductA createProductA(); abstract protected AbstractProductB createProductB();}public class ConcreteFactory1 extends AbstractFactory { @Override protected AbstractProductA createProductA() { return new ProductA1(); } @Override protected AbstractProductB createProductB() { return new ProductB1(); }}public class ConcreteFactory2 extends AbstractFactory { @Override protected AbstractProductA createProductA() { return new ProductA2(); } @Override protected AbstractProductB createProductB() { return new ProductB2(); }}public class Client { public static void main(String[] args) { AbstractFactory factory = new ConcreteFactory1(); AbstractProductA productA = factory.createProductA(); AbstractProductB productB = factory.createProductB(); ... // 結(jié)合使用productA和productB進(jìn)行后續(xù)操作 ... }}
JDK 的 javax.xml.transform.TransformerFactory 組件使用了類似“抽象工廠”模式的設(shè)計(jì),抽象類 TransformerFactory 定義了兩個(gè)抽象方法 newTransformer()和 newTemplates()分別用于生成 Transformer 對(duì)象 和 Templates 對(duì)象,其兩個(gè)子類進(jìn)行了不同的實(shí)現(xiàn),源碼如下(版本 1.8)。
public abstract class TransformerFactory { public abstract Transformer newTransformer(Source source) throws TransformerConfigurationException; public abstract Templates newTemplates(Source source) throws TransformerConfigurationException;}/** * SAXTransformerFactory 繼承了 TransformerFactory */public class TransformerFactoryImpl extends SAXTransformerFactory implements SourceLoader, ErrorListener { @Override public Transformer newTransformer(Source source) throws TransformerConfigurationException { final Templates templates = newTemplates(source); final Transformer transformer = templates.newTransformer(); if (_uriResolver != null) { transformer.setURIResolver(_uriResolver); } return(transformer); } @Override public Templates newTemplates(Source source) throws TransformerConfigurationException { ...... return new TemplatesImpl(bytecodes, transletName, xsltc.getOutputProperties(), _indentNumber, this); }}public class SmartTransformerFactoryImpl extends SAXTransformerFactory { public Transformer newTransformer(Source source) throws TransformerConfigurationException { if (_xalanFactory == null) { createXalanTransformerFactory(); } if (_errorlistener != null) { _xalanFactory.setErrorListener(_errorlistener); } if (_uriresolver != null) { _xalanFactory.setURIResolver(_uriresolver); } _currFactory = _xalanFactory; return _currFactory.newTransformer(source); } public Templates newTemplates(Source source) throws TransformerConfigurationException { if (_xsltcFactory == null) { createXSLTCTransformerFactory(); } if (_errorlistener != null) { _xsltcFactory.setErrorListener(_errorlistener); } if (_uriresolver != null) { _xsltcFactory.setURIResolver(_uriresolver); } _currFactory = _xsltcFactory; return _currFactory.newTemplates(source); }}
該模式主要用于將復(fù)雜對(duì)象的構(gòu)建過(guò)程分解成一個(gè)個(gè)簡(jiǎn)單的步驟,或者分?jǐn)偟蕉鄠€(gè)類中進(jìn)行構(gòu)建,保證構(gòu)建過(guò)程層次清晰,代碼不會(huì)過(guò)分臃腫,屏蔽掉了復(fù)雜對(duì)象內(nèi)部的具體構(gòu)建細(xì)節(jié),其類圖結(jié)構(gòu)如下所示。
該模式的主要角色如下:
?建造者接口(Builder):用于定義建造者構(gòu)建產(chǎn)品對(duì)象的各種公共行為,主要分為 建造方法 和 獲取構(gòu)建好的產(chǎn)品對(duì)象;?具體建造者(ConcreteBuilder):實(shí)現(xiàn)上述接口方法;?導(dǎo)演(Director):通過(guò)調(diào)用具體建造者創(chuàng)建需要的產(chǎn)品對(duì)象;?產(chǎn)品(Product):被建造的復(fù)雜對(duì)象。
其中的導(dǎo)演角色不必了解產(chǎn)品類的內(nèi)部細(xì)節(jié),只提供需要的信息給建造者,由具體建造者處理這些信息(這個(gè)處理過(guò)程可能會(huì)比較復(fù)雜)并完成產(chǎn)品構(gòu)造,使產(chǎn)品對(duì)象的上層代碼與產(chǎn)品對(duì)象的創(chuàng)建過(guò)程解耦。建造者模式將復(fù)雜產(chǎn)品的創(chuàng)建過(guò)程分散到不同的構(gòu)造步驟中,這樣可以對(duì)產(chǎn)品創(chuàng)建過(guò)程實(shí)現(xiàn)更加精細(xì)的控制,也會(huì)使創(chuàng)建過(guò)程更加清晰。每個(gè)具體建造者都可以創(chuàng)建出完整的產(chǎn)品對(duì)象,而且具體建造者之間是相互獨(dú)立的, 因此系統(tǒng)就可以通過(guò)不同的具體建造者,得到不同的產(chǎn)品對(duì)象。當(dāng)有新產(chǎn)品出現(xiàn)時(shí),無(wú)須修改原有的代碼,只需要添加新的具體建造者即可完成擴(kuò)展,這符合“開(kāi)放一封閉” 原則。
相信在拼 SQL 語(yǔ)句時(shí)大家一定經(jīng)常用到 StringBuffer 和 StringBuilder 這兩個(gè)類,它們就用到了建造者設(shè)計(jì)模式,源碼如下(版本 1.8):
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ char[] value; /** * The count is the number of characters used. */ int count; /** * Creates an AbstractStringBuilder of the specified capacity. */ AbstractStringBuilder(int capacity) { value = new char[capacity]; } public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); // 這里完成了對(duì)復(fù)雜String的構(gòu)造,將str拼接到當(dāng)前對(duì)象后面 str.getChars(0, len, value, count); count += len; return this; }}/** * @since JDK 1.5 */public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { public StringBuilder() { super(16); } @Override public StringBuilder append(String str) { super.append(str); return this; } @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }}/** * @since JDK 1.0 */public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence { /** * toString返回的最后一個(gè)值的緩存。在修改StringBuffer時(shí)清除。 */ private transient char[] toStringCache; public StringBuffer() { super(16); } /** * 與StringBuilder建造者最大的不同就是,增加了線程安全機(jī)制 */ @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }}
MyBatis 的初始化過(guò)程使用了建造者模式,抽象類 BaseBuilder 扮演了“建造者接口”的角色,對(duì)一些公用方法進(jìn)行了實(shí)現(xiàn),并定義了公共屬性。XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder 等實(shí)現(xiàn)類扮演了“具體建造者”的角色,分別用于解析 mybatis-config.xml 配置文件、映射配置文件 以及 SQL 節(jié)點(diǎn)。Configuration 和 SqlSessionFactoryBuilder 則分別扮演了“產(chǎn)品” 和 “導(dǎo)演”的角色。即,SqlSessionFactoryBuilder 使用了 BaseBuilder 建造者組件 對(duì)復(fù)雜對(duì)象 Configuration 進(jìn)行了構(gòu)建。
BaseBuilder 組件的設(shè)計(jì)與上面標(biāo)準(zhǔn)的建造者模式是有很大不同的,BaseBuilder 的建造者模式主要是為了將復(fù)雜對(duì)象 Configuration 的構(gòu)建過(guò)程分解的層次更清晰,將整個(gè)構(gòu)建過(guò)程分解到多個(gè)“具體構(gòu)造者”類中,需要這些“具體構(gòu)造者”共同配合才能完成 Configuration 的構(gòu)造,單個(gè)“具體構(gòu)造者”不具有單獨(dú)構(gòu)造產(chǎn)品的能力,這與 StringBuilder 及 StringBuffer 是不同的。
構(gòu)建者模式其核心就是用來(lái)構(gòu)建復(fù)雜對(duì)象的,比如 mybatis 對(duì) Configuration 對(duì)象的構(gòu)建。當(dāng)然,我們也可以把 對(duì)這個(gè)對(duì)象的構(gòu)建過(guò)程 寫(xiě)在一個(gè)類中,來(lái)滿足我們的需求,但這樣做的話,這個(gè)類就會(huì)變得及其臃腫,難以維護(hù)。所以把整個(gè)構(gòu)建過(guò)程合理地拆分到多個(gè)類中,分別構(gòu)建,整個(gè)代碼就顯得非常規(guī)整,且思路清晰,而且 建造者模式符合 開(kāi)閉原則。其源碼實(shí)現(xiàn)如下。
public abstract class BaseBuilder { /** * Configuration 是 MyBatis 初始化過(guò)程的核心對(duì)象并且全局唯一, * MyBatis 中幾乎全部的配置信息會(huì)保存到Configuration 對(duì)象中。 * 也有人稱它是一個(gè)“All-In-One”配置對(duì)象 */ protected final Configuration configuration; /** * 在 mybatis-config.xml 配置文件中可以使用標(biāo)簽定義別名, * 這些定義的別名都會(huì)記錄在該 TypeAliasRegistry 對(duì)象中 */ protected final TypeAliasRegistry typeAliasRegistry; /** * 在 mybatis-config.xml 配置文件中可以使用 標(biāo)簽添加自定義 * TypeHandler,完成指定數(shù)據(jù)庫(kù)類型與 Java 類型的轉(zhuǎn)換,這些 TypeHandler * 都會(huì)記錄在 TypeHandlerRegistry 中 */ protected final TypeHandlerRegistry typeHandlerRegistry; /** * BaseBuilder 中記錄的 TypeAliasRegistry 對(duì)象和 TypeHandlerRegistry 對(duì)象, * 其實(shí)是全局唯一的,它們都是在 Configuration 對(duì)象初始化時(shí)創(chuàng)建的 */ public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); }}public class XMLConfigBuilder extends BaseBuilder { /** 標(biāo)識(shí)是否已經(jīng)解析過(guò) mybatis-config.xml 配置文件 */ private boolean parsed; /** 用于解析 mybatis-config.xml 配置文件 */ private final XPathParser parser; /** 標(biāo)識(shí) 配置的名稱,默認(rèn)讀取 標(biāo)簽的 default 屬性 */ private String environment; /** 負(fù)責(zé)創(chuàng)建和緩存 Reflector 對(duì)象 */ private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // 在 mybatis-config.xml 配置文件中查找 節(jié)點(diǎn),并開(kāi)始解析 parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { //issue #117 read properties first // 解析 節(jié)點(diǎn) propertiesElement(root.evalNode("properties")); // 解析 節(jié)點(diǎn) Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); // 解析 節(jié)點(diǎn) typeAliasesElement(root.evalNode("typeAliases")); // 解析 節(jié)點(diǎn) pluginElement(root.evalNode("plugins")); // 解析 節(jié)點(diǎn) objectFactoryElement(root.evalNode("objectFactory")); // 解析 節(jié)點(diǎn) objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 解析 節(jié)點(diǎn) reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 解析 節(jié)點(diǎn) environmentsElement(root.evalNode("environments")); // 解析 節(jié)點(diǎn) databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 解析 節(jié)點(diǎn) typeHandlerElement(root.evalNode("typeHandlers")); // 解析 節(jié)點(diǎn) mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }}public class XMLMapperBuilder extends BaseBuilder { private final XPathParser parser; private final MapperBuilderAssistant builderAssistant; private final Map sqlFragments; private final String resource; public void parse() { // 判斷是否已經(jīng)加載過(guò)該映射文件 if (!configuration.isResourceLoaded(resource)) { // 處理 節(jié)點(diǎn) configurationElement(parser.evalNode("/mapper")); // 將 resource 添加到 Configuration.loadedResources 集合中保存, // 它是 HashSet 類型的集合,其中記錄了已經(jīng)加載過(guò)的映射文件 configuration.addLoadedResource(resource); // 注冊(cè) Mapper 接口 bindMapperForNamespace(); } // 處理 configurationElement() 方法中解析失敗的 節(jié)點(diǎn) parsePendingResultMaps(); // 處理 configurationElement() 方法中解析失敗的 節(jié)點(diǎn) parsePendingCacheRefs(); // 處理 configurationElement() 方法中解析失敗的 SQL 語(yǔ)句節(jié)點(diǎn) parsePendingStatements(); } private void configurationElement(XNode context) { try { // 獲取 節(jié)點(diǎn)的 namespace 屬性,若 namespace 屬性為空,則拋出異常 String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 設(shè)置 MapperBuilderAssistant 的 currentNamespace 字段,記錄當(dāng)前命名空間 builderAssistant.setCurrentNamespace(namespace); // 解析 節(jié)點(diǎn) cacheRefElement(context.evalNode("cache-ref")); // 解析 節(jié)點(diǎn) cacheElement(context.evalNode("cache")); // 解析 節(jié)點(diǎn),(該節(jié)點(diǎn) 已廢棄,不再推薦使用) parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 解析 節(jié)點(diǎn) resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析 節(jié)點(diǎn) sqlElement(context.evalNodes("/mapper/sql")); // 解析
上述內(nèi)容就是怎么從 Spring及Mybatis框架源碼中學(xué)習(xí)設(shè)計(jì)模式,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。