一、前言
Spring中除了在xml中定义bean,还支持注解方式,我们只需要使用<context:component-scan>标签声名bean所在的路径即可。如下图所示:

在自定义标签解析中,分析了spring如何解析自定义标签,spring会根据自定义标签的命名空间选择对应的命名空间处理器,然后对标签进行解析,本文将以自定义标签<context:component-scan>为例,讲解Spring如何通过解析该标签,将对应package的类注入到容器中。
二、标签解析
承接上文,自定义标签的入口是DefaultBeanDefinitionDocumentReader.parseCustomElement():
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}对于<context:component-scan>标签来说,对应的NamespaceHandler是ContextNamespaceHandler。
NamespaceHandlerSupport:
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}//查询该标签对应的BeanDefinitionParser
@Nullable
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;
}<context:component-scan>对应的BeanDefinitionParser是ComponentScanBeanDefinitionParser:
public BeanDefinition parse(Element element, ParserContext parserContext) {
//获取base-package属性,bean所在的package
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// 创建ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
boolean useDefaultFilters = true;
//对use-default-filters属性进行解析,默认为true,表示使用了注解@Component的bean,会注册到容器中
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}
// 创建scanner,用于扫描类后注解。
ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
//对资源进行筛选的正则表达式,用于过滤bean
if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
}
try {
//如果声明了name-generator属性,则使用给定的beanName生成策略,否则采用默认的策略,使用
//className首字母小写作为beanName
parseBeanNameGenerator(element, scanner);
}
catch (Exception ex) {
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
}
try {
//解析scoped-proxy和scope-resolver属性,平时很少使用,这里不做介绍,有兴趣的读者可以自己研究。
parseScope(element, scanner);
}
catch (Exception ex) {
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
}
//解析配置的类型过滤器属性,包括include-filter和exclude-filter
parseTypeFilters(element, scanner, parserContext);
return scanner;
}ClassPathBeanDefinitionScanner:
//创建ClassPathBeanDefinitionScanner,scanner的功能是扫描指定路径的bean,并注册到容器中。
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
readerContext.getEnvironment(), readerContext.getResourceLoader());
}public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
//如果use-default-filters为true,则注册默认过滤器。
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}ClassPathScanningCandidateComponentProvider:
protected void registerDefaultFilters() {
//支持Component注解
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
//支持ManagedBean注解
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
//支持Named注解
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}从代码中可以知道,默认情况下,spring会扫描带了Component、ManagedBean、Named注解的bean,注入容器。
ComponentScanBeanDefinitionParser:
protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
// Parse exclude and include filter elements.
ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
//获取conponent-scan标签的子标签
NodeList nodeList = element.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
String localName = parserContext.getDelegate().getLocalName(node);
try {
//如果子标签的名称是include-filter,解析该标签,添加IncludeFilter。
if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
scanner.addIncludeFilter(typeFilter);
}
//如果子标签的名称是exclude-filter,解析该标签,添加IncludeFilter。
else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
scanner.addExcludeFilter(typeFilter);
}
}
catch (ClassNotFoundException ex) {
parserContext.getReaderContext().warning(
"Ignoring non-present type filter class: " + ex, parserContext.extractSource(element));
}
catch (Exception ex) {
parserContext.getReaderContext().error(
ex.getMessage(), parserContext.extractSource(element), ex.getCause());
}
}
}
}include-filter和exclude-filter的原理相同,可以支持annotation、assignable的方式判断是否是否注入bean。下面以include-filter作为例子介绍:
protected TypeFilter createTypeFilter(Element element, @Nullable ClassLoader classLoader,
ParserContext parserContext
) throws ClassNotFoundException {
//解析type属性
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
//过滤策略
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
//从annotation的角度过滤bean
if ("annotation".equals(filterType)) {
return new AnnotationTypeFilter((Class<Annotation>) ClassUtils.forName(expression, classLoader));
}
else if ("assignable".equals(filterType)) {
return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader));
}
else if ("aspectj".equals(filterType)) {
return new AspectJTypeFilter(expression, classLoader);
}
else if ("regex".equals(filterType)) {
return new RegexPatternTypeFilter(Pattern.compile(expression));
}
else if ("custom".equals(filterType)) {
Class<?> filterClass = ClassUtils.forName(expression, classLoader);
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
throw new IllegalArgumentException(
"Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
}
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
}
else {
throw new IllegalArgumentException("Unsupported filter type: " + filterType);
}
}由于use-default-filters默认支持使用了Component、Named、ManagedBean的bean,Controller、Service等注解使用了Component注解,因此使用了这两个注解的bean也会被扫描出来,如果我们只关注使用了Controller注解的bean,可以配置include-filter,type=annotion,expression=org.springframework.stereotype.Controller。
到此为止,ClassPathBeanDefinitionScanner已经配置好,下一步是扫描类,并注入bean到容器中。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
//扫描basePackage,使用配置的include-filter、exclude-filter规则,而且类是最外层类、非抽象、非接口。
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//设置bean的作用域
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//注册beandefinition到容器中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}ComponentScanBeanDefinitionParser:
protected void registerComponents(
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
Object source = readerContext.extractSource(element);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
}
// Register annotation config processors, if necessary.
boolean annotationConfig = true;
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
//如果context:component-scan标签中声明annotation-config=true(默认为true),
//用于支持使用autowired、autowired注解注入成员变量
if (annotationConfig) {
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
}
}
readerContext.fireComponentRegistered(compositeDef);
}registerComponents()方法的主要作用:如果context:component-scan标签中声明了annotation-config为true,则在容器中注入以下bean等:
只有配置了annotation-config为true,才能通过注解注入依赖。

上面列出的三个bean都是BeanPostProcessor,执行时机是bean实例化后,用于bean的依赖注入、初始化等。
至此,Spring对<context:component-scan>标签的解析分析完毕。
三、总结
Spring中解析<context:component-scan>的逻辑如下:
- 根据<context:component-scan>的配置内容创建ClassPathBeanDefinitionScanner,设置包含、过滤bean的策略(use-default-filters、exclude-filter、include-filter),beanName的生成策略(name-generator)等;
- 使用创建好的Scanner扫描basePackage目录,并根据步骤1中设置的策略,过滤bean,设置beanName,生成beanDefinition,注册到容器中;
- 如果配置了annotation-config为true,则在容器中注册CommonAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor、ConfigurationClassPostProcessor等BeanPostProcessor,用于支持依赖注解注入bean。