細(xì)說Spring——IoC詳解(注解驅(qū)動開發(fā)之包掃描過濾和FactoryBean)

一、前言

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

二、包掃描的過濾

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

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

    Filter[] includeFilters() default {};

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

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

    @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 {};

    }

我們可以看到內(nèi)部使用FilterType枚舉來表明了當(dāng)前的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上面標(biāo)有@myAnno注解,而我們的配置類中使用excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {myAnno.class})}把標(biāo)有@myAnno注解的組件排除了,現(xiàn)在我們看一下測試的結(jié)果:

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

這里寫圖片描述

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

接下來我們看一下FilterType.ASSIGNABLE_TYPE ,這個是按照類型來過濾的,我們?nèi)匀皇褂蒙厦娴睦?,只不過變化一下過濾的規(guī)則,只修改一下配置類:

@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過濾了,這里看一下測試結(jié)果:

這里寫圖片描述

很明顯Person類已經(jīng)被過濾了。

然后我們來學(xué)習(xí)一下FilterType.CUSTOM,這個需要我們自己定義過濾的規(guī)則,我們需要自己實現(xiàn)一個過濾器,這個過濾器實現(xiàn)TypeFilter接口,看一下我實現(xiàn)的一個過濾器:

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

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

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

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

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

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

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

        return false;
    }
}

上面的實現(xiàn)類中我的過濾邏輯就是過濾全類名中含有“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 {

}

看一下測試結(jié)果:


這里寫圖片描述

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

三、FactoryBean

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

//創(chuàng)建一個Spring定義的工廠bean
public class ColorFactoryBean implements FactoryBean<Color> {
    //返回一個color對象,這個對象會添加到容器中
    //如果該bean是多實例的,就會在創(chuàng)建實例的時候調(diào)用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獲取的是調(diào)用getObject獲得的對象
        Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
        //Object colorFactoryBean2 = applicationContext.getBean("colorFactoryBean");
        System.out.println("獲得是:  " + colorFactoryBean.getClass());
    }

測試結(jié)果:

這里寫圖片描述

我們可以看到我們在打印容器中有哪些組件時,容器中的是colorFactoryBean,可是在我們獲取到colorFactoryBean這個組件時,發(fā)現(xiàn)獲取到的是color,這個功能雖然我們并不常用,但是還是需要了解一下,我們?nèi)绻褪窍胍@得colorFactoryBean,我們只需要:

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

然后獲取到的就是colorFactoryBean

這里寫圖片描述

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容