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()
步骤:
- 获取到标签中
base-package
属性的值,这个值是必须的。(否则会抛出异常,异常信息At least one base package must be specified
)。
- 创建扫描器:ClassPathBeanDefinitionScanner(mybatis中的扫描器就是继承了它)
- 调用核心方法doScan(这里解析并注册了BeanDefinition)
- 注册组件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;
}
}
|
从以下源码可以看到,首先设置是否使用默认过滤器,然后创建扫描器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()
- 扫描出所有满足条件的类,并封装成BeanDefinition
- 循环扫描到的BeanDefinition集合,依次执行操作。
- 解析@Scope注解
- 对不同类型的BeanDefinition做不同处理,@Lazy、@DependOn等注解信息封装就是在这里设置的。
- 校验是否是需要实例化的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的方式,很容易对标签解析功能进行扩展。
技巧
其他的解析类,方法都是一样的。
- 首先根据标签名称找到NamespaceUri,根据SPI加载方式,到对应模块META-INF/spring.handlers文件中查找对应的NamespaceHandler实现类;
- 然后在实现类中的init方法中,找到标签属性对应的BeanDefinitionParser实现类;
- 最后,进入实现类的parse方法,读源码即可。结合断点调试,很容易读懂。
万事开头难,spring源码也一样。刚开始确实挺难理解的,理清了脉络后,再读起来就相对简单很多。