逐行閱讀Spring5.X源碼(番外篇)自定義掃描器, Mybatis是如何利用spring完成Mapper掃描的

? ? ? ?上一篇詳細(xì)講解了spring的掃描器ClassPathBeanDefinitionScanner惠爽,本篇我們我們將模擬mybatis如何通過spring完成Mapper掃描得哆,講解如何通過spring編寫自定義掃描器怯晕。
? ? ? ?既然ClassPathBeanDefinitionScanner完成了spring的掃描功能,我們完全可以繼承這個類來達(dá)到創(chuàng)建自定義掃描器的目的箱吕。

public class CustomScanner extends ClassPathBeanDefinitionScanner {
    public CustomScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
}

? ? ? ?這就是我們的自定義掃描器芥驳,當(dāng)然什么功能也沒有,再次基礎(chǔ)上我們繼續(xù)進(jìn)行擴展茬高,上一篇我們講過ClassPathBeanDefinitionScanner通過includeFilters來過濾符合條件的業(yè)務(wù)類兆旬,那我們增加我們的掃描規(guī)則即可。如何includeFilters在添加我們的掃描規(guī)則怎栽?很簡單丽猬,實現(xiàn)addIncludeFilter即可

public class CustomScanner extends ClassPathBeanDefinitionScanner {
    public CustomScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
    @Override
    public void addIncludeFilter(TypeFilter includeFilter) {
        super.addIncludeFilter(includeFilter);
    }
}

? ? ? ?也就是說,我們只要編寫我們的規(guī)則類TypeFilter 就行了婚瓜,剩下的交給ClassPathBeanDefinitionScanner來完成宝鼓,有讀者嘲諷:“我還以為你要模擬一個ClassPathBeanDefinitionScanner”,答:“我閑的巴刻?mybatis也是利用了ClassPathBeanDefinitionScanner好嗎愚铡?”。
? ? ? ?TypeFilter只是一個接口胡陪,它的實現(xiàn)類在哪里沥寥?spring啟動時會默認(rèn)生成一個ClassPathBeanDefinitionScanner對象,該對象在初始化時會生成一坨默認(rèn)的過濾器(上篇講過):

    protected void registerDefaultFilters() {
        this.includeFilters.add(new AnnotationTypeFilter(Component.class));
        ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();

        try {
            this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean", cl), false));
            this.logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
        } catch (ClassNotFoundException var4) {
        }

        try {
            this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named", cl), false));
            this.logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
        } catch (ClassNotFoundException var3) {
        }

    }

? ? ? ?看到?jīng)]柠座,就是AnnotationTypeFilter啦邑雅!

OK,我們先自定義一個注解:

public @interface MyAnnotation {
}

再生成兩個業(yè)務(wù)類妈经,加上上面的注解:

@MyAnnotation
public class MyClass {
}

@MyAnnotation
public class MyClass2 {
}

測試以下:


public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注冊配置類
        context.register(Config.class);
        CustomScanner customScanner = new CustomScanner(context);
        customScanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
  //將帶有MyAnnotation注解的類掃描到springIOC容器中淮野,并返回掃描的個數(shù)
        int num = customScanner.scan("com.scan");
        System.out.println("掃描的個數(shù):"+num);
        context.refresh();

        System.out.println(context.getBean(MyClass.class));

    }
}
打印結(jié)果:
掃描的個數(shù):2
com.scan.MyClass@1199fe66

? ? ? ?哈哈捧书,簡單吧!其實骤星,這就是spring擴展经瓷,要擴展就得懂spring源碼,歡迎讀者關(guān)注本專題洞难,我將帶大家深入分析并解開spring源碼神秘面紗∮咚保現(xiàn)在java開發(fā),逃不掉spring全家桶队贱!
? ? ? ?spring只能掃描實體類色冀,抽象類和接口是不能掃描的,看上篇講過的源碼:

if (isCandidateComponent(sbd)) {
            if (debugEnabled) {
                    logger.debug("Identified candidate component class: " + resource);
                                }
            //返回BeanDefinition 注冊到 BeanFactory
            candidates.add(sbd);
    }

? ? ? ?isCandidateComponent這個方法判斷 如果是實體類 返回true,如果是抽象類柱嫌,但是抽象方法 被 @Lookup 注解注釋返回true锋恬。

    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return (metadata.isIndependent() && (metadata.isConcrete() ||
                (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
    }

也就是說如果你是接口,isCandidateComponent返回的是false慎式,candidates.add(sbd);就不執(zhí)行了伶氢,自然添加不到IOC容器中,mybatis就是修改了這個方法瘪吏,讓Mapper接口能掃描到IOC容器中的癣防,我們先模擬:

@MyAnnotation
public interface MyInterFace {
    
    @Select("select * from table")
    public void select();
}

public class CustomScanner extends ClassPathBeanDefinitionScanner {
    public CustomScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    public void addIncludeFilter(TypeFilter includeFilter) {
        super.addIncludeFilter(includeFilter);
    }

    @Override
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//如果是接口,則返回true掌眠,會添加到IOC容器中去
            return beanDefinition.getMetadata().isInterface();

    }
}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注冊配置類
        context.register(Config.class);
        CustomScanner customScanner = new CustomScanner(context);
        customScanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
        int num = customScanner.scan("com.scan");
        System.out.println("掃描的個數(shù):"+num);
        context.refresh();
    }
}
打印結(jié)果
掃描的個數(shù):1

然后蕾盯,spring會根據(jù)MyInterFace生成一個動態(tài)代理類,并通過反射拿到你所有的方法蓝丙,拿到方法后再通過反射拿到你的注解信息级遭,還需要我繼續(xù)說嗎?
實際上,mybatis就是這么做的渺尘,不信看mybatis源碼:

ClassPathMapperScanner.java // mybatis的自定義掃描類
   protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

? ? ? ?這就是spring擴展開發(fā)挫鸽,插件開發(fā)就是這個原理吧,前提是你要對spring足夠的掌握鸥跟,歡迎大家關(guān)注本專題丢郊,一起精通spring源碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末医咨,一起剝皮案震驚了整個濱河市枫匾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拟淮,老刑警劉巖干茉,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異很泊,居然都是意外死亡角虫,警方通過查閱死者的電腦和手機沾谓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來上遥,“玉大人搏屑,你說我怎么就攤上這事》鄢” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵亮垫,是天一觀的道長模软。 經(jīng)常有香客問我,道長饮潦,這世上最難降的妖魔是什么燃异? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮继蜡,結(jié)果婚禮上回俐,老公的妹妹穿的比我還像新娘。我一直安慰自己稀并,他們只是感情好仅颇,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碘举,像睡著了一般忘瓦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上引颈,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天耕皮,我揣著相機與錄音,去河邊找鬼蝙场。 笑死凌停,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的售滤。 我是一名探鬼主播罚拟,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼趴泌!你這毒婦竟也來了舟舒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嗜憔,失蹤者是張志新(化名)和其女友劉穎秃励,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吉捶,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡夺鲜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年皆尔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片币励。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡慷蠕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出食呻,到底是詐尸還是另有隱情流炕,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布仅胞,位于F島的核電站每辟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏干旧。R本人自食惡果不足惜渠欺,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望椎眯。 院中可真熱鬧挠将,春花似錦、人聲如沸编整。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闹击。三九已至镶蹋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赏半,已是汗流浹背贺归。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留断箫,地道東北人拂酣。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像仲义,于是被迫代替她去往敵國和親婶熬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359