在 spring 中,为简化 bean 的配置,在 spring-context 模块下提供了包的自动扫描功能,将配置的包及其子包下的所有符合条件的类都注册到 BeanFactory 中。下面来看下具体是怎么实现的。
配置
<context:component-scan base-package="com.icheetor.annotation.service"/>
配置很简单,但背后要实现的功能确比较复杂,先来看看 spring 对这段配置的解析。
解析
在 META-INF/spring.handlers 文件中,找到自定义标签 context 对应的 handler 为 ContextNamespaceHandler,查看 ContextNamespaceHandler#init 方法,name 为 component-scan 对应的解析器为 ComponentScanBeanDefinitionParser,进入 ComponentScanBeanDefinitionParser#parse 方法。
@Override
@Nullable
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);
// 扫描 bean 定义,并注册它们
// 1.配置扫描器
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
// 2.执行扫描,得到 BeanDefinition 并注册到 BeanFactory
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
// 3.注册注解相关后置处理器
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
先是对 basePackage 的解析,此处也支持 "${...}" 占位符,但这种一开始 xml 解析时,由 spring 中的属性解析器 PropertyResolver 可知,所支持的占位字符串很有限。basePackage 也支持多个包的配置,以 "," 或 ";" 分隔即可,将 basePackage 解析为名称为 basePackages 的 String 数组。
此处解析 basePackage 中占位符时采用的策略很宽松,resolvePlaceholders,没有默认值的无法解析占位符将被忽略,并原封不动地传递。
解析完 basePackage,接着进行扫描器的配置,有了路径,还需要工具来执行。
扫描器配置
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
boolean useDefaultFilters = true;
// 使用默认过滤器,默认 true
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}
// Delegate bean definition registration to scanner class.
ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
}
try {
// 解析 name-generator
parseBeanNameGenerator(element, scanner);
}
catch (Exception ex) {
parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
}
try {
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;
}
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");
// registry 即 BeanFactory
this.registry = registry;
if (useDefaultFilters) {
registerDefaultFilters();
}
// 全局 Environment,创建 ApplicationContext 时创建
setEnvironment(environment);
// ApplicationContext 继承 ResourceLoader,当采用 ClassPathXmlApplicationContext,此处 resourceLoader 即 ClassPathXmlApplicationContext
setResourceLoader(resourceLoader);
}
// 注册默认过滤器
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
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 {
this.includeFilters.add(new AnnotationTyp