Spring源码5-BeanDefinition生成

前言

Spring 最重要的功能就是帮助程序员创建对象(也就是IOC),而启动 Spring 就是为创建 Bean 对象做准备,所以我们先明白 Spring 到底是怎么去创建 Bean 的,也就是先弄明白 Bean 的生命周期。 Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的

Spring 在真正去实例化一个 bean 的时候是根据对应的 BeanDefinition 去处理的,所以首先就是要知道 BeanDefinition 是怎么生成的,这个流程也就是 Bean 的扫描流程。

Spring自动扫描注解方式

Spring 容器加载方式主要有以下3种:

  1. xml配置文件方式 ClassPathXmlApplicationContext
  2. 扫描注解方式 AnnotationConfigApplicationContext
  3. 本地配置文件 FileSystemXmlApplicationContext

我们平常使用 Spring可以通过 @ComponentScan 注解可以声明自动注入要扫描的包,Spring 会将该包下所有加了 @Component 注解的 Bean 扫描到 Spring 容器中,也可以 通过 xml 文件来配置要扫描的 Bean,不过现在 SpringBoot 一般都是用 @ComponentScan 注解的方式,所以这里主要介绍自动扫描注解的流程。

Bean扫描流程分析

之前提过,Spring 中的 “Hello world” 如下:

1
2
3
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanScanConfig.class);  
HelloWorldService service = (HelloWorldService) context.getBean("helloWorldService");  
service.sayHello();

上述几行代码实现了 Bean 的扫描和实例化。

首先就是容器的初始化,即:

1
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanScanConfig.class);

容器初始化

来看类 AnnotationConfigApplicationContext 的初始化,类 AnnotationConfigApplicationContext 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
    
    private final AnnotatedBeanDefinitionReader reader;
    private final ClassPathBeanDefinitionScanner scanner;

    public AnnotationConfigApplicationContext() {
        StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
        this.reader = new AnnotatedBeanDefinitionReader(this);
        createAnnotatedBeanDefReader.end();
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

    public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
        this();
        register(componentClasses);
        refresh();
    }
}

一开始,会执行带参数的构造函数,然后调用无参构造函数。StartupStep 是用来统计耗时的,这里不做具体分析。

this.reader = new AnnotatedBeanDefinitionReader(this); 这里用于传入指定 bean 的类的时候使用,会直接进行 BeanDefinition 的注册。注意这里传入的是 this 参数。 实际上,不管是直接指定 bean 的类进行注册还是扫描注册,都是注册到类 DefaultListableBeanFactory 的 beanDefinitionMap 参数上,参数类型为

1
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

DefaultListableBeanFactory 是 this 的父类 GenericApplicationContext 进行初始化的,因为在 Java 中子类初始化时会先初始化父类。

这里主要关注的就是属性

1
private final ClassPathBeanDefinitionScanner scanner;

它就是我们所说的扫描器,我们可以指定扫描某个路径,让其对扫描到的类进行解析。接下来就着重来看 ClassPathBeanDefinitionScanner 的扫描逻辑。

核心扫描方法ClassPathBeanDefinitionScanner#scan

ClassPathBeanDefinitionScanner 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private final BeanDefinitionRegistry registry;

public int scan(String... basePackages) {  
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();  
    doScan(basePackages);  
    if (this.includeAnnotationConfig) {  
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);  
    }  
    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);  
}

可以看到 scan() 方法比较简单,传进来包路径作为参数,调用了 doScan() 方法来完成扫描处理,值得说明的是 Spring 源码中都是类似的命名风格,doXXX 表示具体处理什么。

ClassPathBeanDefinitionScanner 是一个扫描器,这里 registry 是一个接口 BeanDefinitionRegistry,运行的时候实际的值其实就是 DefaultListableBeanFactory。但是这里扫描器只需要完成注册 BeanDefinition 就可以了,所以没必要用 DefaultListableBeanFactory,更符合单一职责。

接下来来看 ClassPathBeanDefinitionScanner#doScan 方法的源码:

 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 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) {  
            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) {  
            }  
            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;  
}

basePackages 即指定的扫描路径。 findCandidateComponents(basePackage) 这个方法就是最核心的扫描逻辑。

ClassPathScanningCandidateComponentProvider#findCandidateComponents :

1
2
3
4
5
6
7
public Set<BeanDefinition> findCandidateComponents(String basePackage) {  
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {  
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);  
    } else {  
        return scanCandidateComponents(basePackage);  
    }  
}

这里会判断我们是否有定义组件索引,来执行不同扫描逻辑。

有索引的扫描流程

这个方法会去判断this.componentsIndex 是否有值,这个就是我们配置的 Bean 索引,它加载的是 META-INF/spring.components中的信息,内容格式形如:

1
com.vitahlin.bean.xxxBean=org.springframework.stereotype.Component

配置之后,就会加载这里的 Bean 组件,不去做路径下的全部扫描。对于项目过于庞大,扫描耗费时间的情况,可以考虑这种方案。一般情况下都走默认的扫描流程。

默认的扫描流程scanCandidateComponents

接着来看默认的扫描流程 ClassPathScanningCandidateComponentProvider#scanCandidateComponents

 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
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {  
    Set<BeanDefinition> candidates = new LinkedHashSet<>();  
    try {  
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;  
        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 {  
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);  
                    if (isCandidateComponent(metadataReader)) {  
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);  
                        sbd.setSource(resource);  
                        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;  
}

其中 scanCandidateComponents 方法就是扫描的核心流程,

构建扫描路径

这里,传入包路径就是字符串,先构造对应的包路径:

1
2
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
    resolveBasePackage(basePackage) + '/' + this.resourcePattern;

这里可以加个 print 语句来查看最终的路径是什么:

1
2
3
4
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
    resolveBasePackage(basePackage) + '/' + this.resourcePattern;  
System.out.println("basePackage:" + basePackage);  
System.out.println("packageSearchPath:" + packageSearchPath);

执行结果:

1
2
basePackage:org.springframework.vitahlin.bean
packageSearchPath:classpath*:org/springframework/vitahlin/bean/**/*.class

这里的处理逻辑是包名前面加上 classpath*: 前缀,后面加上 **/*.class,同时把包名中的 . 替换成 / ,得到搜索路径,最终要匹配查找的是包路径下的 class 文件,包下面可能还有其他文件,但是这里就不管了。

获取包路径下的资源

拿到对应的包路径后,Spring 就用本身的 resource 工具去获取该路径下的资源文件,找出来的其实就是 class 文件的 file 对象。调试信息如下:

可以看到,这里的 Rescource 就是一个个 class 文件的文件对象,接下来就是遍历获取到的 Resource 资源,逐个处理了。

获取类对应的元数据

getMetadataReader 则是根据 Resource 获取类对应的元数据。

什么是元数据?

Metadata is simply data about data. It means it is a description and context of the data. It helps to organize, find and understand data。

上面介绍的大概意思是:元数据是关于数据的数据,元数据是数据的描述和上下文,它有助于组织,查找,理解和使用数据。

Spring 对 MetadataReader 的描述为:

Simple facade for accessing class metadata,as read by an ASM.

大意是通过 ASM 读取 class IO 流资源组装访问元数据的门面接口。反正,这里通过 MetadataReader 我们就能拿到类的信息,比如类的名字,类的注解信息等。这里使用了 ASM 技术。

MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有:

  1. 获取类的名字;
  2. 获取父类的名字;
  3. 获取所实现的所有接口名;
  4. 获取所有内部类的名字;
  5. 判断是不是抽象类;
  6. 判断是不是接口;
  7. 判断是不是一个注解;
  8. 获取拥有某个注解的方法集合;
  9. 获取类上添加的所有注解信息;
  10. 获取类上添加的所有注解类型集合;

判断类是否有@Component注解

拿到类的元信息后,就需要判断这个类是不是一个Bean。我们可能定义了一个类,但是这个类并不是我们需要的 Bean,那么这里是怎么处理的呢?

@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component

大概意思是 @Component 是任何 Spring 管理的组件的通用原型。@Repository@Service@Controller 是派生自 @Component

ClassPathScanningCandidateComponentProvider#isCandidateComponent 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {  
    /** 类和某个类排除过滤器匹配,就忽略 */  
    for (TypeFilter tf : this.excludeFilters) {  
        if (tf.match(metadataReader, getMetadataReaderFactory())) {  
            return false;  
        }  
    }  
    /**  
     * 符合includeFilter的会进行匹配,通过了才是Bean,这里先判断是否有@Component注解。  
     * spring扫描器初始化的时候会注册一个默认的包含过滤器,即类上是否有Component注解的包含过滤器  
     */  
    for (TypeFilter tf : this.includeFilters) {  
        if (tf.match(metadataReader, getMetadataReaderFactory())) {  
            /** 再判断@Condition条件注解 */  
            return isConditionMatch(metadataReader);  
        }  
    }  
    return false;  
}

这里并不是直接判断类上是否存在 @Component 注解,而是去判断类的排除过滤器和包含过滤器

  1. 首先我这个类和任意一个排除过滤器匹配了,那么我就排除这个类;
  2. 如果排除过滤器符合,那么继续判断包含过滤器。即如果要成为一个 bean,一定要符合某个包含过滤器才行。

那么这里为什么是通过判断包含过滤器而不是判断是否有注解 @Component

实际上,在 Spring 的 scanner 初始化的时候会注册一个默认的包含过滤器,我们回过头来看 ClassPathBeanDefinitionScanner 初始化的代码,ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,  
                                      Environment environment, @Nullable ResourceLoader resourceLoader) {  
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");  
    this.registry = registry;  
    /*** 注册默认的包含过滤器 */  
    if (useDefaultFilters) {  
        registerDefaultFilters();  
    }  
    setEnvironment(environment);  
    setResourceLoader(resourceLoader);  
}

继续跟进方法 registerDefaultFiltersClassPathScanningCandidateComponentProvider#registerDefaultFilters

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
protected void registerDefaultFilters() {  
    // 添加默认的Component注解包含过滤器  
    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 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.  
    }  
}

可以看到方法的第一行即添加了默认的包含过滤器。 我们可以新建一个非bean的类来测试下。

非Bean类测试用例

新建一个类Student,但是不加 @Component 注解:

1
2
3
4
5
6
7
package org.springframework.vitahlin.bean;  
  
public class Student {  
    public void hello() {  
        System.out.println("hello Student");  
    }  
}

然后开始断点:

图中可以看到,当前 for 循环处理的是 Student.class 这个类,然后右键 isCandidateComponent -> Evuluate Expression ,得到的结果为 false,即 Spring 不认为 Student 是一个 bean。

所以这里判断包含过滤器即判断类上是否有 @Component 注解,如果有那么就是匹配的,即是一个 Bean。

条件匹配

通过 filter 判断是否有 @Component 注解后,还需要继续判断当前类是否满足条件注解,即 isConditionMatch(metadataReader); 这个方法,这里最终调用的方法是 ConditionEvaluator#shouldSkip ,如图所示:

image.png

这里不细讲判断条件注解的源码,我们只需要大概了解流程如下:

  • 类如果没有 ``@Conditional` 直接,返回 false,表示不跳过
  • 类有@Conditional 注解,获取类上条件注解的类,执行判定

构造 ScannedGenericBeanDefinition

上一步判断完成后,确认类是一个Bean,就会根据元数据开始构造一个 ScannedGenericBeanDefinition

1
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);

跟进 ScannedGenericBeanDefinition 的构造函数ScannedGenericBeanDefinition#ScannedGenericBeanDefinition

1
2
3
4
5
6
7
public ScannedGenericBeanDefinition(MetadataReader metadataReader) {  
    Assert.notNull(metadataReader, "MetadataReader must not be null");  
    this.metadata = metadataReader.getAnnotationMetadata();  
    /*** 一开始只是把class name复制给属性,并没有真正的加载类 */  
    setBeanClassName(this.metadata.getClassName());  
    setResource(metadataReader.getResource());  
}

AbstractBeanDefinition#setBeanClassName 方法处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
	@Nullable
	private volatile Object beanClass;
	
	@Override
	public void setBeanClassName(@Nullable String beanClassName) {
		this.beanClass = beanClassName;
	}
}

注意这里,一开始我们只是把这个类的名字赋给了BeanDefinition

这里的 beanClass 属性为什么是 Object 类型?

因为在这里,一开始我们只是把 bean 的名字赋给了它,当我需要这个类的时候,就需要对类进行实例化,实例化后我就能把实例化后的类型赋给 beanClass 属性,覆盖掉原先 String 类型的 BeanName。所以这个 beanClass 分两种情况:

  1. 一开始的时候是类的名字
  2. 后面实例化后一个类的对象。

判断扫描到的类是不是独立的

1
2
3
4
5
6
if (isCandidateComponent(sbd)) {
	if (debugEnabled) {
		logger.debug("Identified candidate component class: " + resource);
	}
	candidates.add(sbd);
}

构造完 ScannedGenericBeanDefinition 后,这里还会继续判断 Bean 的类型,比如是不是抽象类。这里涉及到一个概念,类是否是独立的

源码注释中是这么说的:

Determine whether the underlying class is independent, i.e. whether it is a top-level class or a nested class (static inner class) that can be constructed independently of an enclosing class.

具体来看方法 ClassPathScanningCandidateComponentProvider#isCandidateComponent

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {  
    AnnotationMetadata metadata = beanDefinition.getMetadata();  
    /**  
     * isIndependent当前类是否顶级类或者嵌套类  
     * isConcrete不是接口,不是抽象类,就返回true  
     * 如果bean是抽象类并且添加类@Lookup注解,那么可以注入  
     */  
    return (metadata.isIndependent() && (metadata.isConcrete() ||  
        // 确实是抽象类,但是有Lookup注解  
        (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));  
}

测试用例

这里涉及到了独立类的概念,新建 UserInterface 接口类:

1
2
3
4
5
6
7
8
package org.springframework.vitahlin.bean;  
  
import org.springframework.stereotype.Service;  
  
@Service  
public interface UserInterface {  
    void sayHello();  
}

然后断点查看 isCandidateComponent 的执行结果,如下:

虽然 UserInterface 加了 @Service 注解,但是它并不是独立的将会被过滤。这个方法就是用来过滤诸如内部类、抽象类等,即不依赖其他类也能正常被构建。因为后续扫描完成,有一个很重要的步骤是实例化,而那些非静态的内部类、接口、抽象类是不能被实例化的,故这里需排除在外。

反之,对 UserService 的判定:

可以看到,结果为 true

判定类是可以注册 BeanDefinition 的,就将生成的 ScannedGenericBeanDefinition 加入到 一开始定义的 BeanDefinition 集合Set<BeanDefinition> candidates 中,那么这里就完成了类的扫描,接下来就就开始逐个遍历扫描得到的 BeanDefinition 集合,根据 BeanDefinition 生成对应的 Bean。

Bean的scope处理

1
2
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);  
sbd.setSource(resource);

这里是处理 BeanDefinitionscope属性,默认是单例属性。

BeanDefinition名字生成

处理完 BeanDefinition 属性后,开始处理 Bean 的名字:

1
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

这行代码,处理了 bean 名字的生成,这里会调用 AnnotationBeanNameGenerator#generateBeanName,根据AnnotationBeanNameGenerator#generateBeanName 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Override  
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {  
    if (definition instanceof AnnotatedBeanDefinition) {  
        String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);  
        if (StringUtils.hasText(beanName)) {  
            // Explicit bean name found.  
            return beanName;  
        }  
    }  
    // Fallback: generate a unique default bean name.  
    // 没有找到@Component注解自定义的value值,则生成一个  
    return buildDefaultBeanName(definition, registry);  
}

这里分为2部分:

  1. 先是判断我们是不是在注解上自定义了 Bean 的名字
  2. 如果没有则根据默认规则生成 Bean 名字

判断注解是不是定义了Bean名字

AnnotationBeanNameGenerator#determineBeanNameFromAnnotation 方法源码:

 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
@Nullable  
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {  
    AnnotationMetadata amd = annotatedDef.getMetadata();  
    Set<String> types = amd.getAnnotationTypes();  
    String beanName = null;  
    for (String type : types) {  
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);  
        if (attributes != null) {  
            Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {  
                Set<String> result = amd.getMetaAnnotationTypes(key);  
                return (result.isEmpty() ? Collections.emptySet() : result);  
            });  
            if (isStereotypeWithNameValue(type, metaTypes, attributes)) {  
                /*** 如果是@Component注解,查看是否有自定义value注解,有的话返回value的值,没有返回null */  
                Object value = attributes.get("value");  
                if (value instanceof String) {  
                    String strVal = (String) value;  
                    if (StringUtils.hasLength(strVal)) {  
                        if (beanName != null && !strVal.equals(beanName)) {  
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent " +  
                                "component names: '" + beanName + "' versus '" + strVal + "'");  
                        }  
                        beanName = strVal;  
                    }  
                }  
            }  
        }  
    }  
    return beanName;  
}

获取当前类上的注解,然后 isStereotypeWithNameValue 方法判断是否存在注解 @Component,如果有的话获取它的 value 值,如果没有注解或者没有 value 值,则返回 null

生成默认的Bean名称

如果没有找到我们自定义的 beanName,就需要生成一个默认的 beanName,AnnotationBeanNameGenerator#buildDefaultBeanName

1
2
3
4
5
6
7
protected String buildDefaultBeanName(BeanDefinition definition) {  
    String beanClassName = definition.getBeanClassName();  
    Assert.state(beanClassName != null, "No bean class name set");  
    String shortClassName = ClassUtils.getShortName(beanClassName);  
    /*** 在这里,如果Bean名称的头两个字符都是大写,那么Bean名称就直接是当前的类名,不然取第一个字母小写的类名 */  
    return Introspector.decapitalize(shortClassName);  
}

ClassUtils.getShortName 就是根据 Bean 的全路径名字获取类名,比如 com.vitahlin.service.HelloSerivce 得到的 shortClassName 就是 HelloService

这里,重点来看Introspector#decapitalize

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static String decapitalize(String name) {  
    if (name == null || name.length() == 0) {  
        return name;  
    }  
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&  
                    Character.isUpperCase(name.charAt(0))){  
        return name;  
    }  
    char chars[] = name.toCharArray();  
    chars[0] = Character.toLowerCase(chars[0]);  
    return new String(chars);  
}

会判断类名字的第一个字符和第二个字符,如果前2个字符全部大写的话,那么直接返回类名;否则将返回首字符小写的类名字。 比如类,AbcService 它的bean名字就是 abcService ,类 ABcService 它的bean名字则是 ABcService

这里总结下 bean 名字的生成规则: 判断该类上的注解 @Component 的 value 值

  • 若该值存在, 则返回
  • 若该值不存在,则调用方法生成默认名字 - 若类名前两个字符都是大写字符则直接返回, - 否则将第一个字符小写返回

Bean的默认值处理

1
2
3
4
/*** 如果扫描得到的Bean不是spring的注解Bean,则为Bean设置默认值,设置Bean的自动注入依赖装配属性等 */  
if (candidate instanceof AbstractBeanDefinition) {  
    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);  
}

主要处理一些 bean 的默认值,比如我们没有设置懒加载选项,就默认设为非懒加载。

判断Bean是否重复

接下来,方法 checkCandidate(beanName, candidate) 会判断类是否重复,最终调用的是ClassPathBeanDefinitionScanner#checkCandidate

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {  
    /*** 判断容器里面是不是已经存在这个beanName */  
    if (!this.registry.containsBeanDefinition(beanName)) {  
        return true;  
    }  
    BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);  
    /*** 这里先不分析,后续分析 */  
    BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();  
    if (originatingDef != null) {  
        existingDef = originatingDef;  
    }  
    if (isCompatible(beanDefinition, existingDef)) {  
        return false;  
    }  
    throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +  
        "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +  
        "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");  
}

方法 isCompatible(beanDefinition, existingDef) 判断已经存在的 Bean 和我现在要生成的是不是兼容, 如果兼容不抛异常,但是返回 false,也不会重新注册 Bean 。

ClassPathBeanDefinitionScanner#isCompatible

1
2
3
4
5
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {  
    return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||  // explicitly registered overriding bean  
        (newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||  // scanned same file twice  
        newDefinition.equals(existingDefinition));  // scanned equivalent class twice  
}

这里其实就是判断两个 BeanDefinition 的 resource 是否一致。一个类会被扫描多次,导致两个不同的 BeanDefinition 实际上是同一个类。这时候如果已经扫描过,那么不重复注册直接跳过。

如果不是兼容的类,但是 BeanName 一样就会抛出我们平常比较常见的异常内容。

不重复就将扫描得到的 Bean 添加到列表中。到这里就完成了整个 Bean 扫描的逻辑,接下来就可以根据得到的 BeanDefinition 集合进行初始化了。

向容器注册扫描得到的BeanDefinition

1
registerBeanDefinition(definitionHolder, this.registry);

这个方法调用的是 BeanDefinitionReaderUtils#registerBeanDefinition

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public static void registerBeanDefinition(  
    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)  
    throws BeanDefinitionStoreException {  
    // Register bean definition under primary name.  
    String beanName = definitionHolder.getBeanName();  
    // 注册到bean工厂  
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  
    // Register aliases for bean name, if any.  
    String[] aliases = definitionHolder.getAliases();  
    if (aliases != null) {  
        for (String alias : aliases) {  
            registry.registerAlias(beanName, alias);  
        }  
    }  
}

看其中的 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()),它是一个接口 BeanDefinitionRegistry,实际上的实现方法是DefaultListableBeanFactory#registerBeanDefinition

 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
62
63
64
@Override  
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  
    throws BeanDefinitionStoreException {  
    Assert.hasText(beanName, "Bean name must not be empty");  
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");  
    if (beanDefinition instanceof AbstractBeanDefinition) {  
        try {  
            ((AbstractBeanDefinition) beanDefinition).validate();  
        } catch (BeanDefinitionValidationException ex) {  
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,  
                "Validation of bean definition failed", ex);  
        }  
    }  
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);  
    if (existingDefinition != null) {  
        if (!isAllowBeanDefinitionOverriding()) {  
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);  
        } else if (existingDefinition.getRole() < beanDefinition.getRole()) {  
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE  
            if (logger.isInfoEnabled()) {  
                logger.info("Overriding user-defined bean definition for bean '" + beanName +  
                    "' with a framework-generated bean definition: replacing [" +  
                    existingDefinition + "] with [" + beanDefinition + "]");  
            }  
        } else if (!beanDefinition.equals(existingDefinition)) {  
            if (logger.isDebugEnabled()) {  
                logger.debug("Overriding bean definition for bean '" + beanName +  
                    "' with a different definition: replacing [" + existingDefinition +  
                    "] with [" + beanDefinition + "]");  
            }  
        } else {  
            if (logger.isTraceEnabled()) {  
                logger.trace("Overriding bean definition for bean '" + beanName +  
                    "' with an equivalent definition: replacing [" + existingDefinition +  
                    "] with [" + beanDefinition + "]");  
            }  
        }  
        // 这里把beanDefinition放到beanMap里面  
        this.beanDefinitionMap.put(beanName, beanDefinition);  
    } else {  
        if (hasBeanCreationStarted()) {  
            // Cannot modify startup-time collection elements anymore (for stable iteration)  
            synchronized (this.beanDefinitionMap) {  
                this.beanDefinitionMap.put(beanName, beanDefinition);  
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);  
                updatedDefinitions.addAll(this.beanDefinitionNames);  
                updatedDefinitions.add(beanName);  
                this.beanDefinitionNames = updatedDefinitions;  
                removeManualSingletonName(beanName);  
            }  
        } else {  
            // Still in startup registration phase  
            this.beanDefinitionMap.put(beanName, beanDefinition);  
            this.beanDefinitionNames.add(beanName);  
            removeManualSingletonName(beanName);  
        }  
        this.frozenBeanDefinitionNames = null;  
    }  
    if (existingDefinition != null || containsSingleton(beanName)) {  
        resetBeanDefinition(beanName);  
    } else if (isConfigurationFrozen()) {  
        clearByTypeCache();  
    }  
}

其中

  • this.beanDefinitionMap.put(beanName, beanDefinition)这行就是把扫描得到的 BeanDefinition 注册到集合中
  • this.beanDefinitionNames.add(beanName);这里还会保存一份 beanName 的集合。

至此就完成了Bean 的扫描整个流程,容器中已经有了对应的 BeanDefinition,后续就可以根据 BeanDefinition 集合进行 bean 的实例化。

结语

大概总结 Bean 的扫描流程如下:

  1. 首先,通过 ResourcePatternResolver 获得指定包路径下的所有.class文件(Spring源码中将此文件包装成了Resource 对象);
  2. 遍历每个 Resource 对象;
  3. 利用 MetadataReaderFactory 解析 Resource 对象得到 MetadataReader(在Spring 源码中 MetadataReaderFactory 具体的实现类为CachingMetadataReaderFactory,MetadataReader 的具体实现类为SimpleMetadataReader)。值得注意的是,CachingMetadataReaderFactory解析某个 .class 文件得到 MetadataReader 对象是利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的 ScannedGenericBeanDefinition 对象,beanClass属性存储的是当前类的名字,而不是class对象
  4. 利用 MetadataReader 进行 excludeFilters 和 includeFilters,以及条件注解@Conditional 的筛选;
  5. 筛选通过后,基于 metadataReader 生成 ScannedGenericBeanDefinition
  6. 再基于 metadataReader 判断是不是对应的类是不是接口或抽象类
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition 加入结果集

BeanDefinition集合生成过程的流程图如下:

Spring Bean扫描.png

署名 - 非商业性使用 - 禁止演绎 4.0