在我们配置的Spring-ApplicationContext文件中,context:component-scan是一个很重要的节点,它主要能帮助我们扫描指定包下面,需要加载到beanFactory容器的类,现在我们就来钻研一下这个扫描和加载的过程。
前面需要注意的地方在http://singleant.iteye.com/blog/1177358中已经说得很清楚了,现在我们只讨论当文件解析器遇到context:component-scan时的操作过程。
component-scan主要分为以下几步:
1.根据context获取到相应的namespaceUri,即http://www.springframework.org/schema/context
2.根据namespaceUri获取到相应的NamespaceHandler或者是className,如果是className,则还需要利用反射进行实例化,得到的是ContextNamespaceHandler,
3.再根据component-scan获取相应的parser,ComponentScanBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
4.根据base-package带进来的参数,分割成数组
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
其中ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS = ",; \t\n";
因此在base-package中的不同包可以用逗号,分号,制表符和换行符分割开,甚至可以混合使用。
5.然后就是新建scanner
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
XmlReaderContext readerContext = parserContext.getReaderContext();
boolean useDefaultFilters = true;
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}
// Delegate bean definition registration to scanner class.
ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
scanner.setResourceLoader(readerContext.getResourceLoader());
scanner.setEnvironment(parserContext.getDelegate().getEnvironment());
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
}
try {
parseBeanNameGenerator(element, scanner);
}
catch (Exception ex) {
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
}
try {
parseScope(element, scanner);
}
catch (Exception ex) {
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
}
parseTypeFilters(element, scanner, readerContext, parserContext);
return scanner;
}
component-scan一共有三个attribute可以配置annotation-config,use-default-filters还有base-package,其中use-default-filters的默认值是true,这样会有三个includeFilter和0个excludeFilter添加,这个后面再详说。
6.然后就可以真正的scanner.doScan(basePackages),进入搜索环节
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
}
//这里因代码太多,删掉了一些log代码和异常捕捉代码
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
//此时传入的 basePackage="com.lh708.controller"
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
//将包名转化为文件夹名 classpath*:com/lh708/controller/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
//这一步会现将classpath*:com/lh708/controller/**/*.class转换为本地文件夹路径,然后搜索文件夹下所有已.class结尾的文件,并封装到Resource返回
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) { //NOTE 1
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) { //NOTE 2
candidates.add(sbd);
}
}
}
}
}
}
return candidates;
}
NOTE 1: 这里会进行Filter检验,上文提到的use-default-filters就与此相关,没有配置的话,默认是true,只会装在三个includeFilters,即三个AnnotationTypeFilter:Component,ManagedBean以及Named
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)) { //NOTE 3
return isConditionMatch(metadataReader); //当次类有被Conditional修饰时,并且满足忽略条件时,也会返回FALSE
}
}
return false;
}
NOTE 2:这里会进行抽象,接口和内部类检查
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
}
@Override
public boolean isConcrete() {
//不可为接口或抽象类
return !(this.isInterface || this.isAbstract);
}
@Override
public boolean isIndependent() {
//不可为内部类
return (this.enclosingClassName == null || this.independentInnerClass);
}
NOTE 3:以Component为例,当这个类有被Component或者其衍生注解如Controller,Service,Repository修饰,返回true
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
综上所述,能被检测并且正常添加到candidates应该满足几个条件
1.在被搜索的包下面
2.不是接口或抽象类,也不是内部类
3.不符合excludeFilters的条件,满足includeFilters的条件
4.被Conditional修饰时,需要看条件决定