spring component-scan filter


(參考的Spring version : 4.1.6.RELEASE)

我們通常會使用component-scan來進行bean的加載,但是它里面的實現機制卻是一知半解。根據原碼來理解一下,可能會更加清晰。

例如,我們通常會使用如下的配置:

application.xml:

<context:component-scan base-package="com.cn.kvn.service,com.cn.kvn.config,com.baidu">
        <context:include-filter type="annotation" expression="com.alibaba.dubbo.config.annotation.Service" />
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

 spring-servlet.xml:

<context:component-scan base-package="com.cn.kvn.controller" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

 

看了下面的解釋,你就能大體知道這些配置具體是怎么控制和生效的了。

 

  • componentScan解析bean的入口為:ComponentScanBeanDefinitionParser#parse(Element element, ParserContext parserContext)
@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

        // Actually scan for bean definitions and register them.
        ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

        return null;
    }

base-package屬性告訴spring要掃描的包,use-default-filters="false"表示不要使用默認的過濾器

 

  • configureScanner會去配置scan時的使用的filter,其中use-default-filters屬性是來控制是否要使用默認的過濾器的。

(默認的過濾器會去解析base-package下的含有@Component注解的類作為bean,其中@Repository, @Service, and @Controller都是@Component的子注解,故,默認的過濾器會將它們全部解析成bean)

原碼中的英文注釋:(ClassPathScanningCandidateComponentProvider#ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment))

useDefaultFilters whether to register the default filters for the @Component, @Repository, @Service, and @Controller stereotype annotations

 

  • 用戶自定義的include-filter和exclude-filter會在ComponentScanBeanDefinitionParser#parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext)方法中被解析加載。

 

  • 在執行Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);的時候,使用過濾器的順序是,exclude-filter優於include-filter。

也就是說,如果同時定義了exclude-filter排除了某類(某個)bean,但是include-filter又將其包含了,則該bean不會被加載到spring容器。

ClassPathScanningCandidateComponentProvider.java
/**
* Determine whether the given class does not match any exclude filter * and does match at least one include filter. * @param metadataReader the ASM ClassReader for the class * @return whether the class qualifies as a candidate component */ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return isConditionMatch(metadataReader); } } return false; }

 

附:過濾規則設置

context:component-scan節點允許有兩個子節點<context:include-filter>和<context:exclude-filter>。filter標簽的type和表達式說明如下:

Filter Type Examples Expression Description include-filter為例
annotation org.example.SomeAnnotation 符合SomeAnnoation的target class

<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>

表示掃描base-package下的類上加了Aspect注解的類,並注冊到spring的bean容器

assignable org.example.SomeClass 指定class或interface的全名

<context:include-filter type="assignable" expression="com.test.scan.StuService"/>

指定掃描StuService類作為bean

aspectj org.example..*Service+ AspectJ語法  
regex org\.example\.Default.* Regelar Expression  
custom org.example.MyTypeFilter Spring3新增自訂Type,實作org.springframework.core.type.TypeFilter  

 

在我們的示例中,將filter的type設置成了正則表達式,regex,注意在正則里面.表示所有字符,而\.才表示真正的.字符。我們的正則表示以Dao或者Service結束的類。

我們也可以使用annotaion來限定,如下:

 <context:component-scan base-package="cn.outofmemory.spring" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>  <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>  </context:component-scan>

 這里我們指定的include-filter的type是annotation,expression則是注解類的全名。

另外context:conponent-scan節點還有<context:exclude-filter>可以用來指定要排除的類,其用法和include-filter一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM