Java 總結

數據結構

數組

Array、Array List...

特點


char[] cs = new char[5];
char[] cs2 = new char[]{'fish','bone'};
cs[0] = "fish";
...
  1. 內存地址連續(xù)购披,使用前需要指定數組長度杖挣。
  2. 有下標。依據下標來獲取刚陡、設置值惩妇。查詢效率高。
  3. 增刪操作效率較低筐乳。(需要考慮下標越界問題歌殃,動態(tài)擴縮容)

總結


增刪操作效率較低主要指:

  1. 新增。
    • 初始長度假如是5蝙云,不停的新增氓皱,當增加到第6個元素時,則需要從觸發(fā)<font color="red">擴容</font>操作(一般會提前觸發(fā)不會等數組滿了才觸發(fā))勃刨,擴容操作則需要重新建立一個數組波材,將之前數據中元素<font color="red">移動</font>過來。
    • 初始長度范圍內的新增效率很高身隐。
  2. 修改廷区。
    • 如果要修改某個元素,比如將數組中'bone'修改為‘bones’則需要遍歷查抄贾铝,然后修改隙轻。
    • 直接通過下標修改效率比較高埠帕。
  3. 刪除。
    • 如果刪除雖然元素不在玖绿,但是內存依然存在敛瓷,且空間是連續(xù)的。比較占用內存镰矿。

綜上所述琐驴,數組適用于,查詢操作較多秤标。修改绝淡、新增、刪除操作較少的定長數據情況。

鏈表

單向鏈表|雙向鏈表(Linked List)

特點


  1. 靈活的空間要求,存儲空間不要求連續(xù)惠奸。
  2. 不支持下標訪問扛拨,查詢效率低泽疆,支持順序遍歷檢索(next)。
  3. 針對增刪操作效率會比較高,只需要操作節(jié)點,無需移動元素丝格。

總結


鏈表適合于新增,刪除操作較多的情況棵譬,由于無法通過下標快速獲取到某個位置元素显蝌,所以對查詢操作不夠友好。

數組订咸、鏈表問題

  1. 在<font color="red">連續(xù)新增</font>100W條數據的情況下曼尊,ArrayList和LinkedList哪一個效率更快?為什么脏嚷?

    • <font color="blue">錯誤回答:</font>LinkedList,因為他是鏈表結構骆撇,鏈表接口新增效率高。ArrayList是數組接口父叙,新增慢神郊。
    • <font color="blue">分析:</font>這個問題主要考慮理解深度,不能簡單因為鏈表新增快就回答LinkedList高每。所有的快慢都是需要分情況的屿岂。
    • <font color="blue"> 正確回答:</font> ArrayList效率高。因為LinkedList每次新增都要new Node()節(jié)點鲸匿。此外還需要操作prev、next兩個節(jié)點阻肩。而ArrayList如果初始就把長度固定則不需要創(chuàng)建那么多對象出來带欢。所以數組的效率就要高于鏈表运授。
    public static void main(String[] args) {
        long startTime=System.currentTimeMillis();   //獲取開始時間
        arrayTest();
        long endTime=System.currentTimeMillis(); //獲取結束時間
        System.out.println("程序運行時間1: "+(endTime-startTime)+"ms");
    
        long startTime1 = System.currentTimeMillis();   //獲取開始時間
        linkTest();
        long endTime1 = System.currentTimeMillis(); //獲取結束時間
        System.out.println("程序運行時間2: "+(endTime1-startTime1)+"ms");
    }
    
    private static void linkTest() {
        LinkedList linkedList = new LinkedList();
        for(int i=0;i<1000000;i++){
            linkedList.add(i);
        }
    }
    
    private static void arrayTest() {
        ArrayList array = new ArrayList(1000000);
        for(int i=0;i<1000000;i++){
            array.add(i);
        }
    }
    
    // 執(zhí)行結果
    程序運行時間1: 18ms
    程序運行時間2: 28ms
    
    Process finished with exit code 0
    

樹(Tree)

二叉樹

只有左右兩個叉的樹結構。

二叉樹中包含:

  • 平衡二叉樹
    • AVL樹乔煞,通過自旋轉實現平衡吁朦。
  • 不平衡二叉樹
    • 二叉查找樹一般都是非平衡的。
  • 完全平衡二叉樹
  • 不完全平衡二叉樹(紅黑樹)渡贾。

特點

  1. 某節(jié)點的左側樹節(jié)點值僅包含小于該節(jié)點的值逗宜。
  2. 某節(jié)點的右側樹節(jié)點值僅包含大于該節(jié)點的值。
  3. 左右樹節(jié)點每個也必須是二叉樹空骚。
  4. 順序排列纺讲。

紅黑樹

自平衡二叉樹。(黑平衡二叉樹)

特點

  1. 每個節(jié)點要么是紅囤屹,要么是黑熬甚。
  2. 根節(jié)點必須是黑色。
  3. 每個葉子節(jié)點【NIL】必須是黑色肋坚。
  4. 每個紅色節(jié)點的子節(jié)點必須是黑色乡括。
  5. 任意節(jié)點向下到任意葉子節(jié)點【NIL】的路徑包含相同數量的黑色節(jié)點(黑平衡二叉樹)。

BTree

平衡多路查找樹智厌,節(jié)點不只是2個诲泌。

每個節(jié)點都會存儲數據。

B+Tree

1kb = 1024b , 1字節(jié) = 8 位铣鹏。 UUID 32位

平衡多路查找樹敷扫,節(jié)點不只是2個。

是對BTree的優(yōu)化吝沫,數據僅存儲在葉子結點呻澜,且葉子節(jié)點以鏈表的形式存在。

集合

TreeMap

特點

  1. 本質是紅黑樹的實現
  2. 有序的

HashMap

數組+鏈表惨险。數組+紅黑樹

鏈表數據長度>8的時候轉化為紅黑樹羹幸。

HashSet

HashSet底層是HashMap,本質是一個沒有重復元素的集合,通過hashmap實現辫愉。

HashSet hashSet = new HashSet();
hashSet.add(1);

//源碼
public HashSet(){
    map = new HashMap<>();
}

public boolean add(E e){
    return map.put(e,PRESENT)==null;
}

TreeSet

TreeSet底層是TreeMap,本質是將數據保存在TreeMap中栅受,key為添加的數據,value是一個固定的值恭朗。

Spring

Spring 特點

Spring 主要為了使開發(fā)人員能夠更專注也業(yè)務本身屏镊,盡可能的減少其他非業(yè)務的東西而出現。

為了實現他這個目的痰腮,Spring 一方面提供了三大核心功能(IOC而芥,DI,AOP)幫助開發(fā)人員管理Bean的生命周期膀值。

另一方面 將自己變成一個萬能膠棍丐,利用自身提供的一些擴展點误辑,能夠對市面上的大部分框架和中間建進行集成,方便使用歌逢。

同時也不斷的優(yōu)化自身巾钉,從最開始的XML到現在的注解一切都是為了方便開發(fā)人員,提高開發(fā)效率秘案。

IOC(控制反轉)

Spring的IOC主要是為了實現對Bean的統(tǒng)一管理砰苍,將Bean的初始化、創(chuàng)建阱高,銷毀都交由Spring的IOC容器來實現赚导。

BeanFactory(IOC容器的定義類)

1. 對IOC的基本行為做了定義。(getBean,constainsBean,isSingleton,isPropotype,isTypeMatch,getType)
2. 主要實現類ListableBeanFactory讨惩、HierarchialBeanFactory(有繼承關系)辟癌、AutowireCapableBeanFactory定義自動裝配規(guī)則。
3. 最終實現類是DefaultListableBeanFactory荐捻,該類包含beanDefinitionMap等集合黍少。

ApplicationContext(高級的IOC容器)

1. 除了提供IOC容器的基本操作外(AbstractApplicationContext)還提供了一些附加功能。
2. 主要實現類ClassPathXMLApplicaitonContext处面、AnnotationConfigApplicaitonContext

BeanDefinition

1. Bean對象的描述類厂置,描述了Bean對象的基本信息以及相互關系(類名、作用域魂角、屬性昵济、依賴關系)。
2. 主要實現類AbstuctBeanDefinition野揪、RootBeanDefinition访忿、GenericBeanDenifition。

BeanDefinitionReader

1. Bean對象的解析類斯稳。
2. AbstractBeanDefinitionReader海铆、XmlBeanDefinitionReader、PropertiesBeanDefinitionReader

IOC的過程實際是為了完成BeanDefinition的注冊挣惰,放在beanDefinitionMap中卧斟。

首先這個過程中初始化的配置定義是多樣的,有XML憎茂、注解等珍语,所以需要使用不同的策略來去讀取。這里就包含兩個主要的核心類ClassPathXMLApplicaitonContext竖幔、AnnotationConfigApplicaitonContext板乙,以及對應的解析器XmlBendifinitionReader、AnnotatedBeanDefinitionReader拳氢,為了方便管理又使用了BeanDefinition來統(tǒng)一配置標準亡驰。


他的核心流程大概分為三個階段:定位—》加載—》注冊晓猛。

Web IOC為例:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--Spring前端控制器饿幅,攔截所有請求-->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

首先從web.xml配置的DispatcherServlet為起點(tomcat啟動時也會調用ContextLoaderListener類contextInitialized方法)凡辱,首先調用HttpServlet類的init()方法,init()方法或者contextInitialized()中會調用initWebApplicationContext方法栗恩,進行初始化透乾,然后會調用到AbstractApplicationContext的refresh()方法,這個也是Spring的核心類磕秤。refresh()方法中會調用obtainFreshBeanFactory()來完成IOC容器的注冊和依賴注入乳乌。在obtainFreshBeanFactory()方法中會調用子類的refreshBeanFactory()方法,進行creatBeanFactory()市咆、loadBeanDefinitions()汉操、doLoadBeanDefinition()、registerBeanDefinition()蒙兰、doRegisterBeanDefinitions()最終調用的是DefaultListableBeanFactory類的registerBeanDefinition()方法來完成Bean的注冊磷瘤。

000-springIOC.png

DI

BeanWrapper( Bean的包裝,包含Bean的屬性搜变、方法采缚,數據 )


核心流程大致分為兩個階段:實例化-》依賴注入

DI操作由BeanFactory的getBean()方法開始,實際調用的是AbstructBeanFactoty()的getBean()方法挠他,在這里會直接進行doGetBean()的操作扳抽。在doGetBean()方法中需要調用一個getSingleton()方法,該方法參數需要一個函數接口殖侵,最終調用的是函數接口實現的creatBean方法AbstractAutowireCapableBeanFactory類(多例的會直接調用creatBean方法),然后會調用doCreateBean()方法贸呢,在這會實例化BeanWrapper對象,這里邊有幾個核心方法createBeanInstance()創(chuàng)建bean實例拢军,addSingletonFactory向容器中緩存單例模式的Bean對象楞陷,以防循環(huán)依賴,populateBean并將Bean實例對象封裝朴沿,并且執(zhí)行DI操作猜谚,填充Bean屬性。initializeBean執(zhí)行一些BeanPostProcessor赌渣,Before,After處理魏铅、AOP,以及InitializingBean的afterPropertiesSet方法坚芜。


循環(huán)依賴

思考:什么是值傳遞览芳?什么是引用傳遞?

Spring中解決循環(huán)依賴其實是利用了引用傳遞的特性鸿竖,允許對象初始化未填充屬性階段先讓其他依賴對象完成引用沧竟,后續(xù)再賦值铸敏。

問題:

哪些情況可以解決?哪些不可以解決悟泵?

  1. 構造函數循環(huán)依賴杈笔。(NO)
  2. 多例Prototype Field Setter 類型循環(huán)依賴。(NO)
  3. 單例Singleton Field Setter 類型循環(huán)依賴糕非。(Yes)

Spring 如何解決循環(huán)依賴

Spring在解決循環(huán)依賴問題時蒙具,主要通過三級緩存,利用引用傳遞的特性朽肥。在依賴注入之前禁筏,將A未完成初始化的bean放入第三級緩存中,key<BeanName,ObjectFactory<?>>(ObjectFactory為函數式接口實現衡招,調用getEarlyBeanReference方法),執(zhí)行DI操作時需要完成B對象實例化篱昔,B對象執(zhí)行DI操作依賴A時,可以直接從三級緩存中獲取到未完成的A對象完成B對象的實例化過程始腾,由于引用傳遞州刽,后續(xù)A對象在實例化完成后,B對象中的A對象也完成實例操作窘茁。

假如A依賴B怀伦,B依賴A,此時先初始化A:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    
    //真正實現向IOC容器獲取Bean的功能山林,也是觸發(fā)依賴注入功能的地方
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        //如果指定的是別名房待,將別名轉換為規(guī)范的Bean名稱
        final String beanName = transformedBeanName(name);
        Object bean;

        // 先從緩存中取是否已經有被創(chuàng)建過的單態(tài)類型的Bean
        // 對于單例模式的Bean整個IOC容器中只創(chuàng)建一次,不需要重復創(chuàng)建
        // 此時A 明顯獲取不到A 對象
        // 當此時為實例化B,為B實例注入A對象調用doGetBean(A)方法情況時驼抹,則可以拿到A對象桑孩,然后返回,具體看最下方代碼
        Object sharedInstance = getSingleton(beanName);
        //IOC容器創(chuàng)建單例模式Bean實例對象
        if (sharedInstance != null && args == null) {
            // 省略...
        }
        else {
            // 省略 ...
            try {
                if (mbd.isSingleton()) {
                    //這里使用了一個匿名內部類框冀,創(chuàng)建Bean實例對象流椒,并且注冊給所依賴的對象
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            //創(chuàng)建一個指定Bean實例對象,如果有父級繼承明也,則合并子類和父類的定義
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            //顯式地從容器單例模式Bean緩存中清除實例對象
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    //獲取給定Bean的實例對象
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
            }
        }
        ...
        return (T) bean;
    }
}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        // ...
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                // ... 
                beforeSingletonCreation(beanName);
                // ...
                try {
                    // 調用函數式接口getObject()實現方法宣虾,等價于 createBean(beanName, mbd, args);
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                catch (IllegalStateException ex) {
                    
                }
                finally {
                    // 執(zhí)行后續(xù)方法
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }
}
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
    implements AutowireCapableBeanFactory {

    //真正創(chuàng)建Bean的方法
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {

        // Instantiate the bean.
        //封裝被創(chuàng)建的Bean對象
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            // 創(chuàng)建實例(此時未賦值屬性)
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        // 獲得過早曝光對象bean。
        final Object bean = instanceWrapper.getWrappedInstance();
        // ...
        //向容器中緩存單例模式的Bean對象温数,以防循環(huán)引用
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                          isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            //向第三級緩存添加 對象實例
            //同時生成 函數接口實現绣硝,一并添加到singletonObjects中,后續(xù)會調用撑刺。
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            //protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            //    Assert.notNull(singletonFactory, "Singleton factory must not be null");
            //    synchronized (this.singletonObjects) {
            //        if (!this.singletonObjects.containsKey(beanName)) {
            //            this.singletonFactories.put(beanName, singletonFactory);
            //            this.earlySingletonObjects.remove(beanName);
            //            this.registeredSingletons.add(beanName);
            //        }
            //    }
            //}
        }

        // Initialize the bean instance.
        //Bean對象的初始化鹉胖,依賴注入在此觸發(fā)
        //這個exposedObject在初始化完成之后返回作為依賴注入完成后的Bean
        Object exposedObject = bean;
        try {
            //將Bean實例對象封裝,并且Bean定義中配置的屬性值賦值給實例對象
            // 此時發(fā)現A依賴于B,回去執(zhí)行B的getBean(),然后同A初始化.
            // 在B實例化到此處時,發(fā)現B引用了A實例,那么就會嘗試獲取A實例對象(調用getBean(A)方法)甫菠。
            populateBean(beanName, mbd, instanceWrapper);
            //初始化Bean對象
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

        if (earlySingletonExposure) {
            //獲取指定名稱的已注冊的單例模式Bean對象
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                //根據名稱獲取的已注冊的Bean和正在實例化的Bean是同一個
                if (exposedObject == bean) {
                    //當前實例化的Bean初始化完成
                    exposedObject = earlySingletonReference;
                }
                //當前Bean依賴其他Bean挠铲,并且當發(fā)生循環(huán)引用時不允許新創(chuàng)建實例對象
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                                      
                }
            }
        }

        // Register bean as disposable.
        //注冊完成依賴注入的Bean
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }
}
//DefaultSingletonBeanRegistry
//Spring利用三級緩存來解決循環(huán)依賴,singletonObjects(1),earlySingletonObjects(2),singletonFactories(3),
// singletonsCurrentlyInCreation 這個緩存也非常重要,在bean開始創(chuàng)建時存入,創(chuàng)建完成后移除,只存儲正在創(chuàng)建中的bean。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 用于存儲完全實例化好的bean 對象寂诱。
    // 首先嘗試從實例化好緩存中 獲取對象 此時(循環(huán)依賴情況)肯定獲取不到拂苹。
    Object singletonObject = this.singletonObjects.get(beanName);
    // 追加判斷 該對象正在創(chuàng)建中(是的)
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 未創(chuàng)建完成,提前曝光的bean對象,用于解決循環(huán)依賴
            // 嘗試從提前曝光的Bean中獲取對象,對于 初始化A(A依賴B,B依賴A) 顯然獲取不到A對象
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 嘗試再從三級緩存單例工廠中獲取刹衫,此時A(A依賴B,B依賴A)很明顯也獲取不到醋寝。 返回null.
                // 此時為初始化A對象(在A對象初始化中,發(fā)現依賴B带迟,初始化B,發(fā)現B依賴A囱桨,然后嘗試獲取A對象情況
                // 能夠從singletonFactory中拿到A對象仓犬。
                // addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 這里 getObject()方法,實際調用getEarlyBeanReference(beanName, mbd, bean)
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

//AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

Aop

Aop核心

  1. 切面 Aspect舍肠,面向規(guī)則搀继,具有相同規(guī)則的<font color="red">方法</font> (methonCache)集合。
  2. 通知 Advice翠语,調用(回調)操作。
  3. 切點 PointCurd,需要代理的具體方法崇猫。
  4. 目標對象 Target泛范,需要代理的對象()。
  5. 代理 Proxy谍夭,JDK 黑滴,CGLIB。
  6. 前置通知紧索、后置通知袁辈、異常通知。

概述

簡單來說珠漂,AOP就是通過一定的規(guī)則晚缩,來當做切面。在Spring 實例化DI階段媳危,基于規(guī)則判斷是否滿足荞彼,滿足則使用代理來代替目標對象。在使用代理對象济舆,調用方法時卿泽,會從methodCache中獲取后續(xù)執(zhí)行鏈(chain),如果存在,則表示改方法有AOP代理需求签夭,執(zhí)行后續(xù)鏈操作齐邦。如果沒有直接利用反射調用目標類方法。

在chain不為null時第租,Spring 使用責任鏈模式措拇,來執(zhí)行操作(有一定的執(zhí)行順序)。

// 緩存切點 攔截器
Map<MethodCacheKey, List<Object>> methodCache;

注意

  1. AOP是基于一定的規(guī)則來匹配方法(通過攔截器實現)慎宾,是方法層面的代理和織入丐吓。但是由于代理針對的是類,所以在DI操作進行規(guī)則驗證時趟据,只需要驗證類的層面滿足就可以券犁。在生成代理對象后調用方法時,才會對方法層面進行驗證(chain)汹碱,通過反射來實現方法的調用粘衬。

  2. AOP采用策略模式,使用JDK(有實現接口時)咳促,和CGLIB兩種方式來實現代理稚新。

  3. AOP操作的開始點是在DI執(zhí)行之后。

    populateBean(beanName, mbd, instanceWrapper);
    //初始化Bean對象  
    // 包含AOP 初始化跪腹,AOP屬于后置處理褂删。 實現了 BeanPostProcessor接口。
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    //AOP 代理類
    public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
         implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        
    }
    

MVC

Spring擴展點

InitializingBean 類

// 在完成bean 的注入(populateBean())之后 調用
// 針對單個bean使用冲茸。
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

調用流程見BeanPostProcessor 類屯阀。

BeanPostProcessor 類

public interface BeanPostProcessor {
    //在Bean 完成定義BeanDefinition、完成依賴注入getBean()->populateBean(),調用無參構造函數完成實例化前執(zhí)行
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    //在Bean 完成定義BeanDefinition噪裕、完成依賴注入getBean(),調用無參構造函數完成實例化后執(zhí)行蹲盘。
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
     
    //  所有的類都會觸發(fā)  
}

調用詳解

//AbstractAutowireCapableBeanFactory    
// getBean() 流程
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
    // ... 省略 無關代碼
    // Instantiate the bean.
    // Allow post-processors to modify the merged bean definition.
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                // 這里處理 特殊的 MergedBeanDefinitionPostProcessor
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                      isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                         "' to allow for resolving potential circular references");
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        // 這里完成 bean 初始化DI 工作
        populateBean(beanName, mbd, instanceWrapper);
        // 這里 initializeBean 方法
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
            throw (BeanCreationException) ex;
        }
        else {
            throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
        }
    }
     // ... 省略無關代碼
    return exposedObject;
}


protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        //下邊 看看這個方法干了點啥
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}


protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
    throws Throwable {

    boolean isInitializingBean = (bean instanceof InitializingBean);
    // 判斷是否 是InitializingBean 的接口,如果實現了InitializingBean膳音,則先執(zhí)行 afterPropertiesSet 方法召衔。
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((InitializingBean) bean).afterPropertiesSet();
                    return null;
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    if (mbd != null && bean.getClass() != NullBean.class) {
       
        String initMethodName = mbd.getInitMethodName();
         //不知道這個initMethodName是什么東西。反正if判斷不會過
        if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

BeanFactoryPostProcessor 類

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    // 所有類都會觸發(fā)
    
    /**
     * Modify(修飾 改變) the application context's internal(內部的) bean factory after its standard(使標準化) initialization.
     * 在應用程序上下文標準工廠類 定義后祭陷,修飾(改變)工廠內部的bean對象 初始化過程苍凛。
     * All bean definitions will have been loaded, but no beans will have been instantiated yet.
        (調用時機)在所有的bean定義都被加載,但沒有實例化之前兵志。
     *  This allows for overriding or adding
     * properties even to eager-initializing beans.
        這允許重寫或添加屬性甚至可以初始化bean醇蝴。
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
    
}

調用時機

//
registerBeanPostProcessors  
//PostProcessorRegistrationDelegate
final class PostProcessorRegistrationDelegate {
    public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

        // Invoke BeanDefinitionRegistryPostProcessors first, if any.
        Set<String> processedBeans = new HashSet<>();
        //beanFactory基本都實現了 BeanDefinitionRegistry 接口
        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
            // 先執(zhí)行 BeanDefinitionRegistryPostProcessor。其實是BeanFactoryPostProcessor的子接口之一
            // public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor =
                        (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                }
                else {
                    regularPostProcessors.add(postProcessor);
                }
            }

            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the bean factory post-processors apply to them!
            // Separate between BeanDefinitionRegistryPostProcessors that implement
            // PriorityOrdered, Ordered, and the rest.
            // 其實是BeanFactoryPostProcessor的子接口之一
            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
            // 其實是BeanFactoryPostProcessor的子接口之一
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
            boolean reiterate = true;
            while (reiterate) {
                reiterate = false;
                postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    if (!processedBeans.contains(ppName)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                        reiterate = true;
                    }
                }
                sortPostProcessors(currentRegistryProcessors, beanFactory);
                registryProcessors.addAll(currentRegistryProcessors);
                invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
                currentRegistryProcessors.clear();
            }

            // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
            invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        }

        else {
            // Invoke factory processors registered with the context instance.
            invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        // 這里才是真正獲取實現類的地方想罕。
        String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

        // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
        // Ordered, and the rest.
        List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
        List<String> orderedPostProcessorNames = new ArrayList<>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        for (String ppName : postProcessorNames) {
            // 判斷是否有執(zhí)行過
            if (processedBeans.contains(ppName)) {
                // skip - already processed in first phase above
            }
            // 是否實現 PriorityOrdered 接口悠栓,實現了優(yōu)先執(zhí)行
            else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
            }
            //是否實現 Ordered 接口霉涨,實現了優(yōu)先執(zhí)行
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }
        // 開始真正執(zhí)行流程
        // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

        // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
        List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
        for (String postProcessorName : orderedPostProcessorNames) {
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

        // Finally, invoke all other BeanFactoryPostProcessors.
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
        for (String postProcessorName : nonOrderedPostProcessorNames) {
            nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

        // Clear cached merged bean definitions since the post-processors might have
        // modified the original metadata, e.g. replacing placeholders in values...
        beanFactory.clearMetadataCache();
    }
}
// PostProcessorRegistrationDelegate
private static void invokeBeanFactoryPostProcessors(
    Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

    for (BeanFactoryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanFactory(beanFactory);
    }
}

FactoryBean

MyBatis

執(zhí)行流程

  1. 解析配置,通過SqlSessionFactoryBuilder創(chuàng)建SqlSessionFactory惭适。
  2. 通過SqlSessionFactory獲取SqlSession,執(zhí)行操作前需要open笙瑟。
  3. 通過SqlSession獲取Mapper代理對象。
  4. 執(zhí)行操作調用excute執(zhí)行器癞志,執(zhí)行xml的sql往枷。
  5. 接收后需要關閉SqlSession。
public void testSelectList() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
    SqlSession session = sqlSessionFactory.openSession(); 
    try {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Blog blog = mapper.selectBlogById(1);
        System.out.println(blog);
    } finally {
        session.close();
    }
}

如何實現Mapper 調用xml方法凄杯?

SqlSessionFactory

SqlSessionFactory在創(chuàng)建過程中错洁,會做一系列的初始化操作,包括插件(分頁插件)的初始化戒突,實體對象和jdbc類型處理器的初始化屯碴,以及最重要的mapper初始化。

對于mapper來說妖谴,實際是由MapperRegistry類(Configuration)的knownMappers屬性來對每一個mapper接口(xml中的namespace)綁定一個MapperProxyFactory對象窿锉,作為后續(xù)sql操作的處理類。

MapperProxyFactory其實并沒有目標對象的概念膝舅,他僅僅只是提供一個入口,這里主要是為了將mapper接口中的方法匹配到xml的sql(statement)窑多,進而執(zhí)行仍稀。(所以無論是否實現mapper接口,均不會調用實現類9∠ⅰ)

public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 用于解析 mybatis-config.xml技潘,同時創(chuàng)建了 Configuration 對象 >>
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 解析XML,最終返回一個 DefaultSqlSessionFactory >>
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
    
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}
public class XMLConfigBuilder extends BaseBuilder {
    
    public Configuration parse() {
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        // XPathParser千康,dom 和 SAX 都有用到 >>
        // 注意 這里直接獲取 mybatis配置文件 configuration 節(jié)點下內容
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 對于全局配置文件各種標簽的解析
            propertiesElement(root.evalNode("properties"));
            // 解析 settings 標簽
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            loadCustomVfs(settings);
            loadCustomLogImpl(settings);
            // 類型別名
            typeAliasesElement(root.evalNode("typeAliases"));
            // 插件
            pluginElement(root.evalNode("plugins"));
            // 用于創(chuàng)建對象
            objectFactoryElement(root.evalNode("objectFactory"));
            // 用于對對象進行加工
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            // 反射工具箱
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            // settings 子標簽賦值享幽,默認值就是在這里提供的 >>
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 創(chuàng)建了數據源 >>
            environmentsElement(root.evalNode("environments"));
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            
            typeHandlerElement(root.evalNode("typeHandlers"));
            // 解析引用的Mapper映射器
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
    
    
    private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                // 不同的定義方式的掃描,最終都是調用 addMapper()方法(添加到 MapperRegistry)拾弃。這個方法和 getMapper() 對應
                // package  包
                if ("package".equals(child.getName())) {
                    String mapperPackage = child.getStringAttribute("name");
                    configuration.addMappers(mapperPackage);
                } else {
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
                    if (resource != null && url == null && mapperClass == null) {
                        // resource 相對路徑
                        ErrorContext.instance().resource(resource);
                        InputStream inputStream = Resources.getResourceAsStream(resource);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        // 解析 Mapper.xml值桩,總體上做了兩件事情 >>
                        mapperParser.parse();
                    } else if (resource == null && url != null && mapperClass == null) {
                        // url  絕對路徑
                        ErrorContext.instance().resource(url);
                        InputStream inputStream = Resources.getUrlAsStream(url);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url == null && mapperClass != null) {
                        // class    單個接口
                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        configuration.addMapper(mapperInterface);
                    } else {
                        throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                    }
                }
            }
        }
    }
    
    
}

public class XMLMapperBuilder extends BaseBuilder {
    public void parse() {
        // 總體上做了兩件事情,對于語句的注冊和接口的注冊
        if (!configuration.isResourceLoaded(resource)) {
            // 1豪椿、具體增刪改查標簽的解析奔坟。
            // 一個標簽一個MappedStatement。 >>
            // 獲取mapperbiao標簽 準備注冊 接口 和語句
            configurationElement(parser.evalNode("/mapper"));
            // 加載 xml 的 <mapepr>節(jié)點資源
            configuration.addLoadedResource(resource);
            // 2搭盾、把namespace(接口)和工廠類綁定起來咳秉,放到一個map。
            // 一個namespace 一個 MapperProxyFactory >>
            bindMapperForNamespace();
        }

        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
    }

    private void bindMapperForNamespace() {
        // 獲取nameSpace 
        String namespace = builderAssistant.getCurrentNamespace();
        if (namespace != null) {
            Class<?> boundType = null;
            try {
                // 獲取接口類型
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException e) {
                //ignore, bound type is not required
            }
            if (boundType != null) {
                if (!configuration.hasMapper(boundType)) {
                    // Spring may not know the real resource name so we set a flag
                    // to prevent loading again this resource from the mapper interface
                    // look at MapperAnnotationBuilder#loadXmlResource
                    configuration.addLoadedResource("namespace:" + namespace);
                    // 添加到 MapperRegistry鸯隅,本質是一個 map澜建,里面也有 Configuration >>
                    configuration.addMapper(boundType);
                }
            }
        }
    }
}
// mybatis 全局配置類
public class Configuration {
    //Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
    // 存儲NameSpace 和 mapper代理工廠對象的對照關系。
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

    public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
    }
    
    //MapperRegistry 類中方法
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                // Map<Class<?>, MapperProxyFactory<?>> 存放的是接口類型,和對應的工廠類的關系
                knownMappers.put(type, new MapperProxyFactory<>(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.

                // 注冊了接口之后炕舵,根據接口何之,開始解析所有方法上的注解,例如 @Select >>
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }

}

OpenSession

openSession 實際返回defaultSqlSession對象幕侠。在這個過程中主要功能有:創(chuàng)建事務帝美、初始化執(zhí)行器、二級緩存晤硕、注冊Excute插件(裝飾器 + 責任鏈+jdk動態(tài)代理模式悼潭,一層一層的裝飾excute對象,并返回結果)舞箍。

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
            // 獲取環(huán)境相關配置 
            final Environment environment = configuration.getEnvironment();
            // 獲取事務工廠
            final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
            // 創(chuàng)建事務  默認jdbc
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            // 根據事務工廠和默認的執(zhí)行器類型舰褪,創(chuàng)建執(zhí)行器 >>
            // 執(zhí)行器中 初始化 插件、二級緩存等
            final Executor executor = configuration.newExecutor(tx, execType);
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            closeTransaction(tx); // may have fetched a connection so lets call close()
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
}
public class Configuration {
    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            // 默認 SimpleExecutor
            executor = new SimpleExecutor(this, transaction);
        }
        // 二級緩存開關疏橄,settings 中的 cacheEnabled 默認是 true
        // mybatis 一級緩存是基于一次會話(openSession),
        // 二級緩存  是基于一個nameSpace,所以每一個命名空間都需要一個CachingExecutor
        if (cacheEnabled) {
            executor = new CachingExecutor(executor);
        }
        // 植入插件的邏輯占拍,至此,四大對象已經全部攔截完畢   裝飾器 + 責任鏈模式+jdk動態(tài)代理
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
    }
    //MyBatis 允許你在映射語句執(zhí)行過程中的某一點進行攔截調用捎迫。默認情況下晃酒,MyBatis 允許使用插件來攔截的方法調用包括:
    //    Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
   //     ParameterHandler (getParameterObject, setParameters)
   //     ResultSetHandler (handleResultSets, handleOutputParameters)
   //     StatementHandler (prepare, parameterize, batch, update, query)
    
}

getMapper

獲取代理對象,執(zhí)行后續(xù)操作窄绒。最終返回代理對象為 MapperProxy贝次。

public class DefaultSqlSession implements SqlSession {
    
    public <T> T getMapper(Class<T> type) {
        // 這里configuration對象是在 創(chuàng)建DefaultSqlSession時傳入的,全局對象引用彰导。
        // mapper的代理對象是存放在 MapperRegistry 類 中 knownMappers屬性中
        // Configuration中包含MapperRegistry的引用(MapperRegistry也包含Configuration引用)
        // 次數實際調用的 是 mapperRefistry 中的方法蛔翅。(符合DDD領域驅動設計規(guī)范)
        return configuration.getMapper(type, this);
    }    
}

//MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

// mapper 接口 動態(tài)代理實現類。
public class MapperProxyFactory<T> {

    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return mapperInterface;
    }

    public Map<Method, MapperMethodInvoker> getMethodCache() {
        return methodCache;
    }

    @SuppressWarnings("unchecked")
    protected T newInstance(MapperProxy<T> mapperProxy) {
        // 1:類加載器:2:被代理類實現的接口位谋、3:實現了 InvocationHandler 的觸發(fā)管理類
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        // 初始化 目標代理類 
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }

}

Sql執(zhí)行 mapper.Method()

由于此時mapper已經是MapperProxy代理對象山析,所以在執(zhí)行方法時會調用MapperProxy的invoke方法

public class MapperProxy<T> implements InvocationHandler, Serializable {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // toString hashCode equals getClass等方法,無需走到執(zhí)行SQL的流程
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else {
                // 提升獲取 mapperMethod 的效率掏父,到 MapperMethodInvoker(內部接口) 的 invoke
                // 普通方法會走到 PlainMethodInvoker(本類的 內部類) 的 invoke
                return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }    

    private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
        try {
            // Java8 中 Map 的方法笋轨,根據 key 獲取值,如果值是 null损同,則把后面Object 的值賦給 key
            // 如果獲取不到翩腐,就創(chuàng)建
            // 獲取的是 MapperMethodInvoker(接口) 對象,只有一個invoke方法
            return methodCache.computeIfAbsent(method, m -> {
                // 判斷 執(zhí)行 method是否是默認方法膏燃。
                // 一般都不是(沒見過需要寫default方法的mapper接口情況)茂卦。
                if (m.isDefault()) {
                    // 接口的默認方法(Java8),只要實現接口都會繼承接口的默認方法组哩,例如 List.sort()
                    try {
                        if (privateLookupInMethod == null) {
                            return new DefaultMethodInvoker(getMethodHandleJava8(method));
                        } else {
                            return new DefaultMethodInvoker(getMethodHandleJava9(method));
                        }
                    } catch (IllegalAccessException | InstantiationException | InvocationTargetException
                             | NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    // 創(chuàng)建了一個 MapperMethod
                    // configuration對象 被傳來傳去
                    return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
                }
            });
        } catch (RuntimeException re) {
            Throwable cause = re.getCause();
            throw cause == null ? re : cause;
        }
    }
    
    
    private static class PlainMethodInvoker implements MapperMethodInvoker {
        private final MapperMethod mapperMethod;

        public PlainMethodInvoker(MapperMethod mapperMethod) {
            super();
            this.mapperMethod = mapperMethod;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
            // SQL執(zhí)行的真正起點
            return mapperMethod.execute(sqlSession, args);
        }
    }

}

核心點

緩存

mybatis中分為一級緩存和二級緩存等龙,一級緩存默認開啟(基于一次會話)处渣。二級緩存需要手動開啟,在nameSpace中<setting>標簽設置蛛砰,cacheEnabled=true罐栈,二級緩存基于一個命名空間。一般新增泥畅,修改荠诬,刪除操作會更新緩存。為了防止遞歸查詢重復處理緩存位仁,還使用了queryStack來統(tǒng)計執(zhí)行次數柑贞。二級緩存模式使用LRU最少使用策略來做內存淘汰。

Spring+Mybatis

Spring集成Mybatis后聂抢,如何完成mapper代理對象的創(chuàng)建和注入钧嘶?

Spring集成Mybatis后,sqlSessionFactory怎么創(chuàng)建琳疏,怎么獲得sqlSession有决,怎么實現打開關閉?

Spring集成Mybatis是通過mybatis-spring.jar包來實現的空盼,基于一些Spring提供的擴展點(FactoryBean,InitializingBean)书幕,mybatis完成了與Spring的集成。

Spring集成Mybatis依賴三個核心配置揽趾。

<!-- Spring bean初始化時會去加載 SqlSessionFactoryBean類實現sqlSessionFactory創(chuàng)建  -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="configLocation" value="classpath:mybatis-config.xml"></property>
    <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.gupaoedu.crud.dao"/>
</bean>

<!--配置一個可以執(zhí)行批量的sqlSession按咒,全局唯一,單例 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg  ref="sqlSessionFactory"></constructor-arg>
    <constructor-arg  value="BATCH"></constructor-arg>
</bean>
  1. SqlSessionFactoryBean主要為了創(chuàng)建SqlSessionFactory但骨。在創(chuàng)建過程中,最終調用mybatis的創(chuàng)建過程智袭,做一系列的初始化操作奔缠,包括插件(分頁插件)的初始化,實體對象和jdbc類型處理器的初始化吼野,以及最重要的mapper初始化(將mapper與MapperProxy代理關聯起來校哎,getMapper時拿到代理對象)。
  2. MapperScannerConfigurer主要是為了掃描xml文件瞳步,通過掃描xml文件獲得dao接口闷哆,并將dao接口注冊到IOC容器中,同時替換IOC容器BeanDefinition目標類单起,統(tǒng)一指向MapperFactoryBean(實現了FactoryBean接口)抱怔。也就是說在DI操作getBean,Dao層實例時,實際調用MapperFactoryBean的getObject方法嘀倒。這個方法最終返回的是MapperProxy代理對象屈留。
  3. SqlSessionTemplate主要為了解決DefaultSqlSession非線程安全的問題局冰。我們在使用DefaultSqlSession時都需要open或者close掉sqlSession。為了在Spring中方便的解決這個問題灌危,提供了SqlSession的另外一個實現類SqlSessionTemplate(線程安全的)康二。線程安全的原因就是因為使用了代理模式,在創(chuàng)建SqlSessionTemplate時實際返回的是SqlSessionInterceptor代理類勇蝙,這里的invoke方法中會open/close操作sqlSession沫勿。

SqlSessionFactory創(chuàng)建

我們在使用Spring集成Mybatis時,會在Spring的配置文件中使用如下配置:

// 實現了 InitializingBean  Spring在完成 Bean 配置后調用 afterPropertiesSet
// 實現了 FactoryBean 在創(chuàng)建bean實例
public class SqlSessionFactoryBean
    implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

    @Override
    public void afterPropertiesSet() throws Exception {
        notNull(dataSource, "Property 'dataSource' is required");
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

        this.sqlSessionFactory = buildSqlSessionFactory();
    }


    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

        final Configuration targetConfiguration;

        XMLConfigBuilder xmlConfigBuilder = null;
        if (this.configuration != null) {
            targetConfiguration = this.configuration;
            if (targetConfiguration.getVariables() == null) {
                targetConfiguration.setVariables(this.configurationProperties);
            } else if (this.configurationProperties != null) {
                targetConfiguration.getVariables().putAll(this.configurationProperties);
            }
        } else if (this.configLocation != null) {
            xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
            targetConfiguration = xmlConfigBuilder.getConfiguration();
        } else {
           
            //默認走這里  先初始化Configuration
            targetConfiguration = new Configuration();

        }

        Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
        Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
        Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

        if (hasLength(this.typeAliasesPackage)) {
            scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
                .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
                .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
        }

        if (!isEmpty(this.typeAliases)) {
            Stream.of(this.typeAliases).forEach(typeAlias -> {
                targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
                LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
            });
        }

        if (!isEmpty(this.plugins)) {
            Stream.of(this.plugins).forEach(plugin -> {
                targetConfiguration.addInterceptor(plugin);
                LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
            });
        }

        if (hasLength(this.typeHandlersPackage)) {
            scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
                .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
                .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
        }

        if (!isEmpty(this.typeHandlers)) {
            Stream.of(this.typeHandlers).forEach(typeHandler -> {
                targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
                LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
            });
        }

        if (!isEmpty(this.scriptingLanguageDrivers)) {
            Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
                targetConfiguration.getLanguageRegistry().register(languageDriver);
                LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
            });
        }
        Optional.ofNullable(this.defaultScriptingLanguageDriver)
            .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

        if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
            try {
                targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
            } catch (SQLException e) {
                throw new NestedIOException("Failed getting a databaseId", e);
            }
        }

        Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

        if (xmlConfigBuilder != null) {
            try {
                xmlConfigBuilder.parse();
                LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
            } catch (Exception ex) {
                throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
            } finally {
                ErrorContext.instance().reset();
            }
        }

        targetConfiguration.setEnvironment(new Environment(this.environment,
                                                           this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
                                                           this.dataSource));
        //配置文件中 通過mapperLocation配置 xml文件位置
        if (this.mapperLocations != null) {
            
            if (this.mapperLocations.length == 0) {
                LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
            } else {
                // 此處進行 掃描加載 
                for (Resource mapperLocation : this.mapperLocations) {
                    if (mapperLocation == null) {
                        continue;
                    }
                    try {
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                                                                                 targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                        // 調用mybatis的方法
                        xmlMapperBuilder.parse();
                    } catch (Exception e) {
                    
                    }
                }
            }
        } else {
            LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
        }

        return this.sqlSessionFactoryBuilder.build(targetConfiguration);
    }
    
    //XMLMapperBuilder類味混,上邊xmlMapperBuilder.parse()产雹。在configutation中 初始化MapperProxyFactory
    public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
            configurationElement(parser.evalNode("/mapper"));
            configuration.addLoadedResource(resource);
            bindMapperForNamespace();
        }

        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
    }

}

Mapper代理注入

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            processPropertyPlaceHolders();
        }

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(lazyInitialization)) {
            scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        scanner.registerFilters();
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
    }
}
// ClassPathBeanDefinitionScanner
public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    // 只做兩件事情,第一掃描全部 mapper.xml  
    // 替換mapper目標對象
    // 注意 首先調用的是子類的doScan方法惜傲。
    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

//ClassPathMapperScanner
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 再調用父類 ClassPathBeanDefinitionScanner doScan方法 掃描接口注冊IOC
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
                    + "' package. Please check your configuration.");
    } else {
        // 修改beanDefinitions 的定義
        processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
        definition = (GenericBeanDefinition) holder.getBeanDefinition();
        String beanClassName = definition.getBeanClassName();
        LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
                     + "' mapperInterface");

        // the mapper interface is the original class of the bean
        // but, the actual class of the bean is MapperFactoryBean
        definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
        // 將實體類 使用mapperFactoryBeanClass類替換洽故。
        // Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
        definition.setBeanClass(this.mapperFactoryBeanClass);

        definition.getPropertyValues().add("addToConfig", this.addToConfig);

        boolean explicitFactoryUsed = false;
        if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
            definition.getPropertyValues().add("sqlSessionFactory",
                                               new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
            explicitFactoryUsed = true;
        } else if (this.sqlSessionFactory != null) {
            definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
            explicitFactoryUsed = true;
        }

        if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
            if (explicitFactoryUsed) {
                LOGGER.warn(
                    () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate",
                                               new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
            explicitFactoryUsed = true;
        } else if (this.sqlSessionTemplate != null) {
            if (explicitFactoryUsed) {
                LOGGER.warn(
                    () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
            explicitFactoryUsed = true;
        }

        if (!explicitFactoryUsed) {
            LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
        definition.setLazyInit(lazyInitialization);
    }
}

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

    @Override
    public T getObject() throws Exception {
        // 調用 sqlSessionTemplete getMapper方法
        return getSqlSession().getMapper(this.mapperInterface);
    }

    @Override
    public Class<T> getObjectType() {
        return this.mapperInterface;
    }

}
//SqlSessionDaoSupport
public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
}
// SqlSessionTemplate
@Override
public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
}

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
                          PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
                                                         new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // open Session
        SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
                                              SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
        try {
            Object result = method.invoke(sqlSession, args);
            if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {         
                sqlSession.commit(true);
            }
            return result;
        } catch (Throwable t) {
            Throwable unwrapped = unwrapThrowable(t);
            if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
                closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                sqlSession = null;
                Throwable translated = SqlSessionTemplate.this.exceptionTranslator
                    .translateExceptionIfPossible((PersistenceException) unwrapped);
                if (translated != null) {
                    unwrapped = translated;
                }
            }
            throw unwrapped;
        } finally {
            // close Session
            if (sqlSession != null) {
                closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            }
        }
    }
}

SqlSessionTemplate

public class SqlSessionTemplate implements SqlSession, DisposableBean {

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
                              PersistenceExceptionTranslator exceptionTranslator) {

        notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        notNull(executorType, "Property 'executorType' is required");

        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
                                                             new Class[] { SqlSession.class }, new SqlSessionInterceptor());
    }   
}

Spring Boot

簡介

  1. Spring的升級版。功能基于Spring盗誊。比如:注解时甚,事件,定時器等
  2. 約定優(yōu)于配置的體現產物哈踱』氖剩基于約定的規(guī)則簡化了一些常用配置。
    1. 引入Web-Stater默認內置tomcat啟動开镣,創(chuàng)建MVC刀诬,提供配置文件。
    2. 自動注入規(guī)則邪财。
  3. Starter啟動
  4. 去XML配置(Spring 提供)
    1. @Component/@Controller/@Service陕壹。(省略<bean>)
    2. @Configuration/@Component-Scan/@Import。(去XML核心)
    3. @Enable驅動树埠。(重點)
  5. @Conditional條件注解糠馆。(重點 Spring 4.X)
  6. EnableAutoConfiguration自動裝配。

特性:Stater怎憋、自動裝配又碌、SpringBoot-Cli、Actuator

Enable驅動

自動完成相關組件的Bean配置绊袋。

核心是通過@Import或者其他方式向IOC容器注入Bean毕匀。

是Spring去XML配置,約定優(yōu)于配置的集中體現癌别,通過配置Enable皂岔,使得開發(fā)者不用關注大量的配置內容。

@EnableScheduling

正常加載:

public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
    private static final String ASYNC_EXECUTION_ASPECT_CLASS_NAME = "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect";

    public AnnotationDrivenBeanDefinitionParser() {
    }

    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);
      
        // .... 省略無關代碼
        if (registry.containsBeanDefinition(
            "org.springframework.context.annotation.internalScheduledAnnotationProcessor"
        )) {
            parserContext.getReaderContext().error(
                "Only one ScheduledAnnotationBeanPostProcessor may exist within the context.", 
                source);
        } else {
            builder = BeanDefinitionBuilder.genericBeanDefinition(
                "org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor"
            );
            builder.getRawBeanDefinition().setSource(source);
            scheduler = element.getAttribute("scheduler");
            if (StringUtils.hasText(scheduler)) {
                builder.addPropertyReference("scheduler", scheduler);
            }
            registerPostProcessor(
                parserContext, builder, 
                "org.springframework.context.annotation.internalScheduledAnnotationProcessor"
                );
        }

        parserContext.popAndRegisterContainingComponent();
        return null;
    }
}

@Enable驅動:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class) // Improt導入類
@Documented
public @interface EnableScheduling {

}

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }

}

自動裝配

在Spring Boot 項目中存在一系列的Stater組件规个,通過在pom.xml中引入就可以直接通過@Autowired注入使用jar包中的類凤薛。比如RedisTemplete姓建。這種形式叫做自動裝配。

實現自動裝配需要考慮幾個問題缤苫。

  1. 如果需要直接注入速兔,那么前提是IOC容器中要有該實例對象。那么IOC容器是怎么做到實例化的活玲?

    這里可以做一個猜想涣狗,想要將類交給IOC容器那么基于正常的方式來說需要這樣做

    @Configuration
    public class AutoConfiguration{
        
        @Bean
        public RedisTemplete redistemplete(){
            return new RedisTemplete();
        }
        
        ....
    }
    
  2. 如何告訴Spring需要加載哪些配置類?

動態(tài)裝載

Spring 提供了兩種方式來實現動態(tài)配置的裝載舒憾。

  1. Selector: ImportSelector镀钓、 DeferredImportSelector
  2. Registory:ImportBeanDefinitionRegistrar

我們通過實現ImportSelector接口selectImports()方法來返回要加載的配置類,這樣可以實現配置類或者Bean對象的動態(tài)裝配镀迂。

通過動態(tài)裝載我們可以將配置類交給Spring去掃描丁溅,完成依賴注入。

那么Spring如何知道我的配置類到底在哪里探遵?—》約定優(yōu)于配置

Spring定義規(guī)則:將需要掃描的配置類通過配置文件的形式(spring.factories)放在META-INF目錄下窟赏。

Spring-Boot通過@EnableAutoConfiguration注解來進行掃描加載。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //導入掃描類
public @interface EnableAutoConfiguration {
    ...
}
------------------------------------------
public class AutoConfigurationImportSelector implements DeferredImportSelector, ... {
    ....
        
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        ...
        //掃描配置
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

----------------------------------------
//最終調用 SpringFactoriesLoader類的loadSpringFactories方法
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";    

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        Enumeration<URL> urls = (classLoader != null ?
                                 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}


SpringFactoriesLoader其實類似SPI(擴展點)

SPI機制

Service provider interface

需要滿足以下條件

  • 創(chuàng)建目錄META-INF/services
  • 在目錄下創(chuàng)建擴展點的全路徑名
    • 文件中要寫擴展點箱季,類的實現
    • 文件編碼UTF-8
  • ServiceLoader去加載

條件控制

@Condition一系列注解涯穷。

官方的Stater(Spring-boot-stater-XXX)不需要額外的spring.factories文件,在spring核心包中統(tǒng)一進行配置藏雏。所以官方的配置類主要是通過@Condition注解來進行條件加載的拷况。

手寫Stater組件

  1. pom.xml引入Sping-boot jar包(需要用到注解@Configuration/@Condition等注解)同時設置<option>屬性。

  2. 引入外部化配置掘殴。

    • 創(chuàng)建配置對象使用@ConfigurationProperties注解赚瘦,同時定義配置前綴。

      @ConfigurationProperties(prefix = "fish.bone")
      public class propertiesConfig(){
          
          private Sting url = "test URL";
          
          ...
      }
      
    • 外部化配置提示奏寨。

      1. 導入jar包(spring-boot-configuration-processor)
    • META-INF目錄下創(chuàng)建additional-spring-configuration-metadata.json文件

      {
        "properties": [
          {
            "name": "server.compression.enabled", //配置名稱 spring.application.port
            "description": "Whether response compression is enabled.", // 配置描述  啟動端口
            "defaultValue": false //設置默認值
          },
          {
            "name": "server.compression.excluded-user-agents",
            "description": "Comma-separated list of user agents for which responses should not be compressed."
          }
         ]
      }
      
  1. 編寫配置類

    • 配置類加入@EnableConfigurationProperties注解加載外部化配置對象蚤告。
  2. 創(chuàng)建spring.factories文件

    Condition 除了注解外 還可以在 MATA-INF目錄下創(chuàng)建 spring-autoconfigure-metadata.properties 來定義注解條件。

MySQL

執(zhí)行流程

sql->緩存(8.0停用)->解析器->預處理器->優(yōu)化器->執(zhí)行計劃->執(zhí)行器

索引機制

Innodb 使用B+Tree作為數據結構服爷。

聚集索引

聚集索引是指數據庫表行中數據的物理順序與鍵值的邏輯(索引)順序相同,有且僅有一個获诈,因為一個表的物理順序只有一種仍源。自增主鍵索引。

為什么推薦使用自曾主鍵作為索引舔涎,就是因為他的順序與表的物理順序一致笼踩。當我這個索引為聚集索引時,對表的新增修改刪除影響會降到最小亡嫌,拿新增來舉例他肯定是在末尾新增嚎于。而非自增主鍵來說我就是一個隨機的接口掘而,當插入到中間位置時,有可能會造成整個索引結構的重新排列于购。

覆蓋索引

覆蓋索引袍睡,表示查詢的數據剛好是索引字段中包含。比如A+B聯合索引肋僧,在查詢select a,b斑胜、a、b 時均能遇到覆蓋索引嫌吠。

索引下推

對于存儲引擎來說止潘,存儲引擎只能通過索引來進行數據過濾。當聯合索引為AB辫诅, where a = '' and b='%as' 此時b用不到索引凭戴,正常來說存儲引擎會只判斷a條件,然后返回server進行過濾炕矮。但是實際情況是會直接過濾ab兩個條件狮杨,這種情況就是索引下推。

回表

非主鍵索引查詢狮暑,且沒有用到覆蓋索引的情況都會產生回表庭呜。

數據庫事務

事務的四大特性

原子性 Atomicity (undolog)

隔離性 Isolation (加鎖、快照 LBCC+MVCC)

持久性 Durability (Redolog+雙寫緩沖)

一致性 Consistency

undolog(回滾日志钢颂,事務回滾),Redolog(執(zhí)行日志+雙寫緩沖钞它,崩潰恢復)。

增刪改操作會自動開始事務殊鞭。手動開始事務:begin; start trancation;事務開始后斷開連接會自動回滾事務遭垛。

一個事務持有的鎖會在事務結束后釋放

事務并發(fā)的三大問題(讀一致性問題)

臟讀()

事務開啟,但未提交的數據操灿,叫做臟數據锯仪。

一個事務讀取到了其他事務未提交的數據,造成前后兩次讀不一致情況趾盐。

不可重復讀(undate/delete)

一個事務讀取到其他事務已提交的數據庶喜,造成在一個事務里邊兩次查詢到不一致的情況。

幻讀(insert)

一個事務讀取到其他事務已提交插入的數據救鲤,造成的前后兩次讀不一致久窟。(只有插入已提交的情況才叫做幻讀)

事務隔離級別

Read Uncommitted(RU未提交讀) -- 沒有解決任何問題

一個事務能夠讀取到其他事務沒有提交的數據,會出現臟讀本缠。

Read Committed(RC已提交讀) -- 解決臟讀問題

一個事務能夠讀取到另外一個事務已提交的數據斥扛。沒有解決可重復讀問題。

Repeatable Read(RR可重復讀) -- 解決不可重復讀問題

一個事務中多次讀取同樣的數據結果是一樣的丹锹,未解決幻讀問題稀颁。

Serializable(串行化)

能夠解決所有問題

如何解決事務并發(fā)的讀一致性問題

  1. 在讀取數據前加鎖芬失,防止其他事務對其修改。(性能太差匾灶,讀操作都要加鎖棱烂,太粗暴 LBCC Lock Based Concurrency Control )
  2. 生成一個時間點的快照,并為這個快照來提供一定級別的一致性讀取粘昨。(MVCC Multi Version Concurrency Control)多版本并發(fā)控制垢啼。

MVCC核心思想

  • 一個事務能夠看到的數據版本。
    1. 第一次查詢前已經提交的事務的修改张肾。
    2. 本事務的修改芭析。
  • 一個事務不能看到的數據版本。
    1. 在本事務第一次查詢之后創(chuàng)建的事務(事務ID比我的事務ID大)
    2. 未提交的事務的修改吞瞪。

MVCC實現原理

DB_TRX_ID: 插入或更新的最后一個事務的ID馁启,事務編號是自動遞增的。

DB_ROLL_PTR:回滾指針芍秆。(刪除版本)


mvcc-1.png
mvcc-2.png
mvcc-3.png

數據庫鎖

表鎖與行鎖

鎖定粒度惯疙、加鎖效率、沖突概率妖啥、并發(fā)性能霉颠。

加鎖方式

lock tables xxx read , lock tables xxx write ,unlock tables

共享鎖(行鎖)

Shared Lock

共享鎖又稱為讀鎖,簡稱S鎖荆虱。顧名思義S鎖就是多個事務對同一條數據可以共享一把鎖蒿偎,都能訪問,但是不能修改怀读。

加鎖方式

select * from table where id = 1 lock in share mode ; commit / rollback;

排它鎖(行鎖)

Exclusive lock

排它鎖又稱謝所诉位,簡稱X鎖。排它鎖不能與其他鎖并存菜枷,如果一個事務獲取了一行數據的排它鎖苍糠,那么其他事務將不能再獲取該行的鎖,只有獲取了排它鎖的事務可以對這一行數據進行讀取和修改啤誊。

加鎖方式

自動:delete / update / insert table 都會默認加鎖X鎖岳瞭。

手動: select * from table where id = 1 for update ; commit / rollback;

意向共享鎖(Intention Shared/Exclusive Lock )

意向鎖分為意向共享鎖,意向排它鎖蚊锹。因為為表加鎖的前提是表中任意一行都沒有被鎖住寝优,這也就意味著加表鎖之前需要先判斷是否有其他行鎖存在。為了提高加表鎖的效率枫耳,使用了意向鎖,在加共享鎖和排它鎖的時候需要先為表加上一個意向共享孟抗、排他鎖迁杨。這樣只需要判斷表有沒有意向鎖就可以快速的鎖表钻心。

意向鎖由存儲引擎自己維護。


鎖的粒度铅协。

lock-1.png

記錄鎖

鎖的粒度捷沸,行鎖鎖住了一條記錄(索引)。

對于要鎖住的行在表中存在的情況狐史,就是記錄鎖痒给。比如數據庫有id為1的一行數據。加鎖通過 select * from table where id = 1 for update ;此時的鎖就是記錄鎖骏全。

間隙鎖

[圖片上傳中...(Lock-2.png-79d15f-1620027539643-0)]

  1. 間隙鎖與間隙鎖不沖突苍柏。(在不新增的情況下,間隙鎖都能夠加鎖成功姜贡。但是如果要新增則不能成功试吁。)
  2. 間隙鎖只阻塞插入。
  3. Gap Lock只在RR中存在楼咳。

鄰間鎖

Lock-4.png

由此可見查詢大于5小于9的情況下熄捍,系統(tǒng)鎖住了大于4小于等于10的范圍,這樣就解決了幻讀的問題母怜。所以InnoDB在RR狀態(tài)就做到了對于幻讀的解決余耽。

Spring Cloud NetFlix

LoadBlance

Ribbon

OpenFeign

Eureka

Config

Gateway

Spring Cloud Alibaba

Doubbo

Nacos

Zookeeper

Sentine

Seata

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市苹熏,隨后出現的幾起案子碟贾,更是在濱河造成了極大的恐慌,老刑警劉巖柜裸,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缕陕,死亡現場離奇詭異,居然都是意外死亡疙挺,警方通過查閱死者的電腦和手機扛邑,發(fā)現死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铐然,“玉大人蔬崩,你說我怎么就攤上這事〔笫睿” “怎么了沥阳?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長自点。 經常有香客問我桐罕,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任功炮,我火速辦了婚禮溅潜,結果婚禮上,老公的妹妹穿的比我還像新娘薪伏。我一直安慰自己滚澜,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布嫁怀。 她就那樣靜靜地躺著设捐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪塘淑。 梳的紋絲不亂的頭發(fā)上萝招,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音朴爬,去河邊找鬼即寒。 笑死,一個胖子當著我的面吹牛召噩,可吹牛的內容都是我干的母赵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼具滴,長吁一口氣:“原來是場噩夢啊……” “哼凹嘲!你這毒婦竟也來了?” 一聲冷哼從身側響起构韵,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤周蹭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后疲恢,有當地人在樹林里發(fā)現了一具尸體凶朗,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年显拳,在試婚紗的時候發(fā)現自己被綠了棚愤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡杂数,死狀恐怖宛畦,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情揍移,我是刑警寧澤次和,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站那伐,受9級特大地震影響踏施,放射性物質發(fā)生泄漏石蔗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一畅形、第九天 我趴在偏房一處隱蔽的房頂上張望抓督。 院中可真熱鬧,春花似錦束亏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阳液,卻和暖如春怕敬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帘皿。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工东跪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鹰溜。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓虽填,卻偏偏與公主長得像,于是被迫代替她去往敵國和親曹动。 傳聞我的和親對象是個殘疾皇子斋日,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內容

  • http://www.reibang.com/p/a07d1d4004b0 源問題集合 Java基礎 1、Li...
    mualex閱讀 3,294評論 3 81
  • 為記錄阿里的電面經歷兔港,特與大家分享,崗位是JAVA研發(fā)工程師仔拟。 一面主要問題如下: 1)首先自我介紹 2)數據結構...
    Java黎先生閱讀 18,492評論 1 6
  • 記錄阿里的電面經歷衫樊,特與大家分享,崗位是JAVA研發(fā)工程師理逊。 一面主要問題如下: 1)首先自我介紹 2)數據結構算...
    飛奔小碼農閱讀 264評論 0 0
  • 為記錄阿里的電面經歷橡伞,特與大家分享,崗位是JAVA研發(fā)工程師晋被。 一面主要問題如下: 1)首先自我介紹 2)數據結構...
    Java架構師Carl閱讀 674評論 0 4
  • 今天感恩節(jié)哎兑徘,感謝一直在我身邊的親朋好友。感恩相遇羡洛!感恩不離不棄挂脑。 中午開了第一次的黨會藕漱,身份的轉變要...
    迷月閃星情閱讀 10,567評論 0 11