細說Spring——IoC詳解(注解驅動開發之包掃描過濾和FactoryBean)

一、前言

上一篇博客(細說Spring——IoC詳解(注解驅動開發之Bean的注入))中簡單的介紹了將組件注入容器的三種方法,這次我們就了解一下如何在包掃描時將不想要的組件排除,或者只添加特定的組件,然后我們學習一下FactoryBean的作用,不知道FactoryBean的可以參考一下:細說Spring——IoC詳解(FactoryBean、方法注入和方法替換)

二、包掃描的過濾

使用@ComponentScan指定要掃描的包,和使用xml配置的包掃描大致類似使用excludeFilters屬性添加排除的組件,使用includeFilters屬性添加只要的組件,但是要使includeFilters生效,必須先將useDefaultFilters屬性設置為false,和xml配置類似,如果使用的是jdk8以上的版本,可以定義多個@ComponentScan,如果jdk8以下的版本,可以使用@ComponentScans,來裝多個@ComponentScan達到相同的效果。

下面我們主要看一下怎么添加excludeFiltersincludeFilters屬性,我們先看一下這兩個屬性的源碼是什么:

    Filter[] includeFilters() default {};

    /**
     * Specifies which types are not eligible for component scanning.
     * @see #resourcePattern
     */
    Filter[] excludeFilters() default {};

我們可以看到這個個屬性的參數都是Filter數組,這里的Filter是在@ComponentScan包里的一個內部注解,我們看一下源碼:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    @interface Filter {

        /**
         * The type of filter to use.
         * <p>Default is {@link FilterType#ANNOTATION}.
         * @see #classes
         * @see #pattern
         */
        FilterType type() default FilterType.ANNOTATION;

        /**
         * Alias for {@link #classes}.
         * @see #classes
         */
        @AliasFor("classes")
        Class<?>[] value() default {};

        /**
         * The class or classes to use as the filter.
         * <p>The following table explains how the classes will be interpreted
         * based on the configured value of the {@link #type} attribute.
         * <table border="1">
         * <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr>
         * <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td>
         * <td>the annotation itself</td></tr>
         * <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td>
         * <td>the type that detected components should be assignable to</td></tr>
         * <tr><td>{@link FilterType#CUSTOM CUSTOM}</td>
         * <td>an implementation of {@link TypeFilter}</td></tr>
         * </table>
         * <p>When multiple classes are specified, <em>OR</em> logic is applied
         * &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
         * <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
         * following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
         * their respective methods will be called prior to {@link TypeFilter#match match}:
         * <ul>
         * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
         * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
         * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
         * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
         * </ul>
         * <p>Specifying zero classes is permitted but will have no effect on component
         * scanning.
         * @since 4.2
         * @see #value
         * @see #type
         */
        @AliasFor("value")
        Class<?>[] classes() default {};

        /**
         * The pattern (or patterns) to use for the filter, as an alternative
         * to specifying a Class {@link #value}.
         * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ},
         * this is an AspectJ type pattern expression. If {@link #type} is
         * set to {@link FilterType#REGEX REGEX}, this is a regex pattern
         * for the fully-qualified class names to match.
         * @see #type
         * @see #classes
         */
        String[] pattern() default {};

    }

我們可以看到內部使用FilterType枚舉來表明了當前的Filter是按照什么過濾的,這里常用的有三種:

  • FilterType.ANNOTATION:按照注解來過濾bean
  • FilterType.ASSIGNABLE_TYPE :按照給定的類型
  • FilterType.CUSTOM:按照自己給定的過濾器過濾

下面我們就挨個展示一下這三種的用法。首先是FilterType.ANNOTATION,這個是按照注解來過濾,首先看一下在配置類:

@ComponentScan(value = "com.jiayifan.bean", excludeFilters = {
    @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {myAnno.class}),
    //@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
    //@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)
})


@Configuration//告訴spring這是一個配置類
public class MainConfig {

}

我們用包掃描往容器中注入組件,這里我就注入的兩個組件
Person

@Component
public class Person {}

Blue

@Component
@myAnno
public class Blue {
}

注意這里的Blue上面標有@myAnno注解,而我們的配置類中使用excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {myAnno.class})}把標有@myAnno注解的組件排除了,現在我們看一下測試的結果:

@Test
public void importTest() {
     printBeans();
}

這里寫圖片描述

可以看到果然沒有blue這個組件。

接下來我們看一下FilterType.ASSIGNABLE_TYPE ,這個是按照類型來過濾的,我們仍然使用上面的例子,只不過變化一下過濾的規則,只修改一下配置類:

@ComponentScan(value = "com.jiayifan.bean", excludeFilters = {
    //@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {myAnno.class}),
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Person.class})
    //@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)
})


@Configuration//告訴spring這是一個配置類
public class MainConfig {

}

可以看到我把Pserosn過濾了,這里看一下測試結果:

這里寫圖片描述

很明顯Person類已經被過濾了。

然后我們來學習一下FilterType.CUSTOM,這個需要我們自己定義過濾的規則,我們需要自己實現一個過濾器,這個過濾器實現TypeFilter接口,看一下我實現的一個過濾器:

/**
 * Created by Yifan Jia on 2018/6/12.
 * 自定的掃描規則
 */
public class MyTypeFilter implements TypeFilter{
    /**
     *
     * @param metadataReader 讀取到的當前正在掃描的類的信息
     * @param metadataReaderFactory 可以獲取到其他任何類信息
     * @return
     * @throws IOException
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        //獲取當前類的直接信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        //獲取當前正在掃描的類的信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        //獲取當前類的資源信息(類的路徑)
        Resource resource = metadataReader.getResource();

        //獲取當前類的全類名
        String className = classMetadata.getClassName();

        System.out.println("classMetadata:    " + className);

        if(className.contains("B")) {
            return true;
        }

        return false;
    }
}

上面的實現類中我的過濾邏輯就是過濾全類名中含有“B”的類,我們看一下配置類:

package com.jiayifan.config;

/**
 * Created by Yifan Jia on 2018/6/12.
 * 配置類代理xml配置文件
 */

@ComponentScan(value = "com.jiayifan.bean", excludeFilters = {
    //@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {myAnno.class}),
    // @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Person.class})
    @ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)
})


@Configuration//告訴spring這是一個配置類
public class MainConfig {

}

看一下測試結果:


這里寫圖片描述

我們在過濾器中還實現了將掃描到的類名打印出來的功能,可以看到我的com.jiayifan.bean包中的類,然后掃描帶有@Component的類加入到容器中,但是又將全類名中含有“B”字母的類排除,所以就只剩下person組件了。

三、FactoryBean

還記得上一篇博客中最后說除了三種常用方法可以注入組件外,我們還有一種方法可以向容器中注入組件嗎,就是使用FactoryBean
首先我們需要先實現一個自己的FactoryBean

//創建一個Spring定義的工廠bean
public class ColorFactoryBean implements FactoryBean<Color> {
    //返回一個color對象,這個對象會添加到容器中
    //如果該bean是多實例的,就會在創建實例的時候調用getObjectType方法
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean....getObject");
        return new Color();
    }


    public Class<?> getObjectType() {
        return Color.class;
    }

    //該bean是否是單實例的
    public boolean isSingleton() {
        return true;
    }
}

然后我們將這個FactoryBean注入容器:

@Configuration
public class MainConfig2 {
    @Bean
    public ColorFactoryBean colorFactoryBean() {
        return new ColorFactoryBean();
    }
}

然我我們看一下測試類:

    @Test
    public void importTest() {
         printBeans();
    }

    private void printBeans() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for(String name : beanDefinitionNames) {
            System.out.println(name);
        }
        //工廠bean獲取的是調用getObject獲得的對象
        Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
        //Object colorFactoryBean2 = applicationContext.getBean("colorFactoryBean");
        System.out.println("獲得是:  " + colorFactoryBean.getClass());
    }

測試結果:

這里寫圖片描述

我們可以看到我們在打印容器中有哪些組件時,容器中的是colorFactoryBean,可是在我們獲取到colorFactoryBean這個組件時,發現獲取到的是color,這個功能雖然我們并不常用,但是還是需要了解一下,我們如果就是想要獲得colorFactoryBean,我們只需要:

  Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
  //前面加一個&
  Object colorFactoryBean2 = applicationContext.getBean("&colorFactoryBean");
  System.out.println("獲得是:  " + colorFactoryBean.getClass());
  System.out.println("獲得是:  " + colorFactoryBean2.getClass());

然后獲取到的就是colorFactoryBean

這里寫圖片描述

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容