BeanDefinitionParser

警告
本文最后更新于 2020-11-23,文中内容可能已过时。

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


本文是对 Spring程序入口和XML解析 的知识点扩展,详细解析其中提到的BeanDefinitionParser的作用,以及一些典型的XML方式解析的实现类。

1 BeanDefinitionParser接口的定义


1
2
3
4
public interface BeanDefinitionParser {
	@Nullable
	BeanDefinition parse(Element element, ParserContext parserContext);
}

可以看到,BeanDefinitionParser只有一个方法,负责BeanDefinitaion的解析。参数有节点元素、解析上下文对象。

在Spring中有很多内置的BeanDefinitionParser实现类(之所以说内置,因为它也是可以扩展的),包括XML方式解析、注解方式解析等。

注意
ParserContext中封装了BeanDefinitionRegistry对象,用于BeanDefinition的注册。

关于此类的实例从哪里注册、parse方法从哪里调入进来的, Spring程序入口和XML解析 中有结尾处有详细说明。

2 ComponentScanBeanDefinitionParser实现流程解析


2.1 ComponentScanBeanDefinitionParser中的属性

ComponentScanBeanDefinitionParser是BeanDefinitionParser的一个实现,用于解析<context:component-scan/>标签的属性。

这个类中定义了一些常量,这些常量是<context:component-scan/>中所有可能用到的属性。比如最常用到的base-package属性。

前面有提到,parse方法的参数中有Element。在解析Element时,会对Element的多个属性进行识别及处理。这些常量的作用就在于此,用来区分Element的不同属性,并做特定的处理。 以下是ComponentScanBeanDefinitionParser中定义的常量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {

	private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";

	private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";

	private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";

	private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";

	private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";

	private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";

	private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";

	private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";

	private static final String INCLUDE_FILTER_ELEMENT = "include-filter";

	private static final String FILTER_TYPE_ATTRIBUTE = "type";

	private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
}

2.2 主流程方法parse()

步骤:

  1. 获取到标签中base-package属性的值,这个值是必须的。(否则会抛出异常,异常信息At least one base package must be specified)。
  2. 创建扫描器:ClassPathBeanDefinitionScanner(mybatis中的扫描器就是继承了它)
  3. 调用核心方法doScan(这里解析并注册了BeanDefinition)
  4. 注册组件registerComponents(ClassPathBeanDefinitionScanner中,最终是一个空方法,可以不看,BeanDefinition的注册在doScan中做了)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
    public BeanDefinition parse(Element element, ParserContext parserContext) {
		// 获取base-package属性
		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);
		// TODO 重点:这里执行了具体的解析逻辑。
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}
}

2.3 ConfigureScanner()

从以下源码可以看到,首先设置是否使用默认过滤器,然后创建扫描器ComponentScanBeanDefinitionParser,之后对其他属性进行一些设置。(关注源码中关键点就好,不然会越陷越深

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
    protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
		// 解析配置的属性,use-default-filters如果未定义,默认为true
		boolean useDefaultFilters = true;
		if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
			useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
		}
		// 创建扫描器,其实mybatis中的扫描原理就来自于此,mybatis中的ClassPathMapperScanner继承了ClassPathBeanDefinitionScanner
		// Delegate bean definition registration to scanner class.
		ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
		// 下面的代码是对标签中其他属性的一些设置,其实就是把标签及其属性以java类的描述形式体现。
		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) {
			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());
		}

		parseTypeFilters(element, scanner, parserContext);

		return scanner;
	}
}

2.4 核心方法doScan()

  1. 扫描出所有满足条件的类,并封装成BeanDefinition
  2. 循环扫描到的BeanDefinition集合,依次执行操作。
    1. 解析@Scope注解
    2. 对不同类型的BeanDefinition做不同处理,@Lazy、@DependOn等注解信息封装就是在这里设置的。
    3. 校验是否是需要实例化的BeanDefinition,如果为true,则将BeanDefinition包装成BeanDefinitionHolder(这个类里封装了实例名称、别名信息)

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		// 用来装所有扫描到的类的BeanDefinition对象。
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			// TODO 重点看:查找候选组件,封装成BeanDefinition。
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				// 获取到ScopeMetadata,如果类中有@Scope注解,则会将值封装到这个实例中。然后设置到BeanDefinition中。
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				// 对不同类型的BeanDefinition做不同的处理。
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				// 对不同类型的BeanDefinition做不同的处理,@Lazy、@DependOn等注解信息封装就是在这里设置的。
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 一些注解信息的封装
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				// 校验是否是需要实例化的BeanDefinition,如果为true,则将BeanDefinition包装成BeanDefinitionHolder(这个类里封装了实例名称、别名信息)
				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;
	}
}

2.5 FindCandidateComponents原理解析

最终会执行到scanCandidateComponents()方法。从下面源码中可以看到,实际上就是根据<context:component-scan/>标签配置的base-package来扫描对应路径下的文件输入流,读取类文件信息。进而拿到Class对象,封装成BeanDefinition对象。然后向上返回,直到注册到BeanDefinitionRegistry中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			//根据路径扫描
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			// 获取指定包下匹配到的所有的资源对象。里面封装了InputStream,用来读取类文件。
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						// 封装成ScannedGenericBeanDefinition,这里封装了包下所有的类信息
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						if (isCandidateComponent(metadataReader)) {
							// 创建BeanDefinition对象,将
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
							// 判断是否是候选组件,如果是,就添加BeanDefinition对象
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}
}

3 AspectJAutoProxyBeanDefinitionParser


AspectJAutoProxyBeanDefinitionParser是对aop标签的aop:aspectj-autoproxy/的支持
源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		// 对aspectj-autoproxy属性的支持
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		// 添加IncludePatterns
		extendBeanDefinition(element, parserContext);
		return null;
	}
}

会走到这里:

1
2
3
4
5
6
7
8
9
public abstract class AopConfigUtils {
	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		// 注入此类 AnnotationAwareAspectJAutoProxyCreator,本质还是一个BeanPostProcessor,
		// 实现了BeanPostProcessor的子接口:SmartInstantiationAwareBeanPostProcessor
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}
}

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法中,注册了AnnotationAwareAspectJAutoProxyCreator的支持,这个类会在AOP知识点中详细阐述

4 AnnotationConfigBeanDefinitionParser

在其源码 parse() 中有这么一段代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		Object source = parserContext.extractSource(element);
		// TODO 重点看下,逻辑相对简单,注册一些内置注解解析支持
		Set<BeanDefinitionHolder> processorDefinitions =
				AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
        // 省略...
		return null;
	}
}

进入 registerAnnotationConfigProcessors():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class AnnotationConfigUtils {
    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		// ApplicationContext是实现了BeanDefinitionRegistry的,这里将ApplicationContext进行强转。
		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		// 省略...
		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

		// ConfigurationClassPostProcessor注册,支持注解@Configuration
		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			// 这里注入了一个BeanFactoryPostProcessor
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
		// AutowiredAnnotationBeanPostProcessor注册,支持@Autowired
		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
		// 省略...
		return beanDefs;
	}
}

可以看到,这里注册了 ConfigurationClassPostProcessor 和 AutowiredAnnotationBeanPostProcessor。

这些都是内置的 BeanDefinitionPostProcessor 实现类,对应注解都是常用的,有必要看一下。

5 总结


spring对XML的解析,就是通过NameSpaceHandler中注册的BeanDefinitionParser进行处理的。通过SPI的方式,很容易对标签解析功能进行扩展。

技巧

其他的解析类,方法都是一样的。

  1. 首先根据标签名称找到NamespaceUri,根据SPI加载方式,到对应模块META-INF/spring.handlers文件中查找对应的NamespaceHandler实现类;
  2. 然后在实现类中的init方法中,找到标签属性对应的BeanDefinitionParser实现类;
  3. 最后,进入实现类的parse方法,读源码即可。结合断点调试,很容易读懂。

万事开头难,spring源码也一样。刚开始确实挺难理解的,理清了脉络后,再读起来就相对简单很多。