Spring源码-类扫描器-ClassPathBeanDefinitionScanner

我们在Spring源码-ApplicationContext中介绍了ApplicationContext都做了什么,其中会对容器中的BeanDefinition对应的class进行初始化,那么BeanDefinition是怎么产生的呢?其中ClassPathBeanDefinitionScanner是做什么用的呢?

ClassPathBeanDefinitionScanner类扫描器,作用通过对制定的basepackage进行扫描,创建、注册BeanDefinition。见类图:
ClassPathBeanDefinitionScanner类图

见下面代码,在无参构造方法中初始化ClassPathBeanDefinitionScanner,之后调用scan(basePackages)将basePackages下的class文件生成BeanDefiniton同时注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public AnnotationConfigApplicationContext(String... basePackages) {
//无参构造函数,初始化reader和scanner,在父类GenericApplicationContext的无参构造方法初始化(beanFactory = new DefaultListableBeanFactory()),
this();
//扫描basePackages并且加载BeanDefinition
scan(basePackages);
//刷新上下文
refresh();
}

//this()方法的内部
public AnnotationConfigApplicationContext() {
//IOC容器初始化注解BeanDefinition的读取器
this.reader = new AnnotatedBeanDefinitionReader(this);
//IOC容器初始化类扫描器
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

scan的解析

ClassPathBeanDefinitionScanner.scan()–>ClassPathBeanDefinitionScanner.doScan(),doScan方法的逻辑如下

  1. 文件生成BeanDefinition
    1. 针对指定的Basepackage找对应的文件,即Resource
    2. 将Resource转换成BeanDefinition注册到容器中
      1. 用spring-asm将Resource生成MetatDataReader
      2. 判断class是否符合容器注入条件(利用typefilter.match)
      3. 生成BeanDefinition,加入到返回值中返回
  2. 处理BeanDefinition
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
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) {
//通过文件找到符合规则的BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
//处理BeanDefinition
for (BeanDefinition candidate : candidates) {
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);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

查找并且生成BeanDefinition

主要的方法是findCandidateComponents,通过指定的basePackage生成BeanDefinition,其中关键之一是PathMatchingResourcePatternResolver,里面会有目录的匹配引擎来找打牌basePackage下所有符合规则的类。

下面的代码是整个findCandidateComponents的逻辑,其中getResourcePatternResolver()就是获取PathMatchingResourcePatternResolver

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
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
//扫描制定的basePackage
return scanCandidateComponents(basePackage);
}
}

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//解析basePackage,生成绝对地址ClassPath*:/xx/xx/*/**.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//通过PathMatchingResourcePatternResolver找到该目录下的Reousrce即文件
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
......
for (Resource resource : resources) {
//resource生成BeanDefinition 暂且忽略,后面会详细解析
......
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}

文件生成Resouces

getResourcePatternResolver()对应的是PathMatchingResourcePatternResolver类,getResources流程如下

  1. findCandidateComponents()方法中,先给basePackage先加上classPath*:,调用getResources方法
  2. 走到findPathMatchingResources所在的分支处理locationPattern,分离出rootDirPath和subPattern
  3. 再次调用getResources(rootDirPath)方法回到上面的方法走到else分支调用findAllClassPathResources方法
  4. 在findAllClassPathResources中调用doFindAllClassPathResources,加载RootDir对应的rootDirResources
  5. 继续回到<2>中的findPathMatchingResources,遍历rootDirResources,这里主要执行else分支doFindPathMatchingFileResources
  6. 通过rootDirPath和subPattern(即 */**等)找到所有符合规则的Class对应的Resource,过程是:
    • doFindPathMatchingFileResources–>doFindMatchingFileSystemResources
    • doFindMatchingFileSystemResources–>retrieveMatchingFiles
    • retrieveMatchingFiles–>doRetrieveMatchingFiles递归执行直到找到所有符合规则的File,并且在doFindMatchingFileSystemResources生成FileSystemResource
1
2
3
4
5
//basePackage先加上 classPath*:
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//通过PathMatchingResourcePatternResolver找到该目录下的Reousrce即文件
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
//包含ClassPath*:
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
//包含特殊字符 * ? {}在findPathMatchingResources中会去掉这些特殊字符,在调用getResources,走到else的分支
return findPathMatchingResources(locationPattern);
}
else {
//调用doFindAllClassPathResources加载所有的类
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
......
}
}
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
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);//eg: classpath*:com/ericliu/spring/**/**/*.class-->classpath*:com/ericliu/spring/
String subPattern = locationPattern.substring(rootDirPath.length());//eg: classpath*:com/ericliu/spring/**/**/*.class--> **/**/*.class
//继续调用getResources-->这时候因为classpath*:已经被替换掉会走到findAllClassPathResources分支,
// 在findAllClassPathResources里加载rootDirPath对应的Reousrce,因为是目录所以数组有几个basePackage就有几个location
// 如:URL [file:<绝对路径>/<rootDirPath>/]
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
//根据rootDirUrl的类型调用不同的逻辑,bundle:// ,vfs , *.jar
if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
if (resolvedUrl != null) {
rootDirUrl = resolvedUrl;
}
rootDirResource = new UrlResource(rootDirUrl);
}
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
}
else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
}
else {
//主要是执行这里的逻辑,去rootDirResource查找符合subPattern的resource(文件)
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isTraceEnabled()) {
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}

doRetrieveMatchingFiles查找符合规则的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Searching directory [" + dir.getAbsolutePath() +
"] for files matching pattern [" + fullPattern + "]");
}
for (File content : listDirectory(dir)) {
String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
//如果是目录,且和fullPattern匹配递归去子目录开始查找
if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
if (!content.canRead()) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
"] because the application is not allowed to read the directory");
}
}
else {
doRetrieveMatchingFiles(fullPattern, content, result);
}
}
if (getPathMatcher().match(fullPattern, currPath)) {
result.add(content);
}
}
}

Reosurce生成BenDefinition

scanCandidateComponents方法中循环上面生成的Resource数组,生成BeanDefinition,其中需要

  1. 获取metadataReader
  2. 判断resource是否符合我们的注入条件
  3. 生成ScannedGenericBeanDefinition
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
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//解析basePackage,生成绝对地址ClassPath*:/xx/xx/*/**.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//通过PathMatchingResourcePatternResolver找到该目录下的Reousrce即文件
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
......
for (Resource resource : resources) {
......
if (resource.isReadable()) {
try {
//1.获取MetadataReader,这里是CachingMetadataReaderFactory,通过SimpleMetadataReaderFactory生成SimpleMetadataReader(resource,defaultResourceLoader.getClassLoader)
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//判断是否符合条件的Component
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
/**
* isCandidateComponent(sbd)为true
* isIndependent不是内部类(内部类对应的外部类为空,且没有static内部类)
* isConcrete:不是借口和抽象类 || 是抽象类,但是metadata有Lookup注解
* 加入到结果集
*/
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
//
candidates.add(sbd);
}
else {
......
}
}
else {
......
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
......
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}

获取Resource对应的MetadataReader

  1. 这里是CachingMetadataReaderFactory类,它继承了SimpleMetadataReaderFactory调用super.getMetadataReader得到SimpleMetadataReader
  2. SimpleMetadataReader构造方法SimpleMetadataReader(resource,defaultResourceLoader.getClassLoader)
  3. SimpleMetadataReader通过spring-asm来操作class文件字节码解析resource(resource通过io读成byte具体如何解析见jvm对class类的解析)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//SimpleMetadataReaderFactory
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}

//SimpleMetadataReader
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
//观察者
SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
getClassReader(resource).accept(visitor, PARSING_OPTIONS);
this.resource = resource;
this.annotationMetadata = visitor.getMetadata();//获取MetaData
}

调用isCandidateComponent方法,通过MetaDataReader来判断其是否符合注入条件,我们通过ClassPathBeanDefinitionScanner的构造方法层层跟进到registerDefaultFilters方法时候就能知道spring的includeFilters支持那些默认的注解见下面,(如果要实现自定义注解要实现TypeFilter和matcher方法具体见Spring源码-实现自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected void registerDefaultFilters() {
//Component
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
//javax.annotation.ManagedBean
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
......
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
//javax.inject.Named
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
......
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}

isCandidateComponent的方法注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
//typteFilter.match返回true
if (tf.match(metadataReader, getMetadataReaderFactory())) {
//判断condintional是否符合条件,注意里面实现的方法名是shouldSkip这里没有注解或者符合条件返回false
return isConditionMatch(metadataReader);
}
}
return false;
}

生成BeanDefinition,这里是ScannedGenericBeanDefinition,设置Resource和Source属性,再次isCandidateComponent判断是否符合条件如果符合直接加入到结果candidates中(不是内部类同时 不是接口和抽象类,或者是接口但是方法有Lookup注解)

处理BeanDefinition

回到doScan方法,在执行完findCandidateComponents方法回去了需要注册的BeanDefiniton之后,我们要继续填充beanDefiniton的属性,并且注册到registry,见代码:

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
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) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//从definition的Annotated获取Scope注解,根据scope注解获取ScopeMetadata
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//生成beanName,从Commponent,或者javax.annotation.ManagedBean、javax.inject.Named的value取,或者系统默认生成
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//设置beanDefinition的默认属性,设置是否参与自动注入
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
//通过注解的MetaData设置属性,用来覆盖默认属性如 lazyInit,Primary,DependsOn,Role,Description属性
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//注册BeanDefinition
// 1.!this.registry.containsBeanDefinition(beanName)
// 2.如果beanName存在,beanDefinition和existingDef兼容,说明不用再次注册
// (不是ScannedGenericBeanDefinition or source相同 or beanDefinition==existingDefinition)
// 3.如果beanName存在,还不兼容抛异常ConflictingBeanDefinitionException
if (checkCandidate(beanName, candidate)) {
//创建BeanDefinitionHolder
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//放到beanDefinitions结果集
beanDefinitions.add(definitionHolder);
//注册到registry中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}