(參考的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一致。