前言
本节将正式介绍Spring源码细节,将讲解Bean生命周期。请注意,虽然我们不希望过于繁琐地理解Spring源码,但也不要认为Spring源码很简单。在本节中,我们将主要讲解Spring 5.3.10版本的源代码。如果您看到的代码与我讲解的不同,也没有关系,因为其中的原理和业务逻辑基本相同。为了更好地理解,我们将先讲解Bean的生命周期,再讲解Spring的启动原理和流程,因为启动是准备工作的一部分。
题外话
目前在该版本中,引入了一个名为jfr的JDK技术,类似于Java飞行日志(JFL),也称为飞行数据记录器(Black Box)技术。具体作用不再详细阐述,读者可以参考此文:JFR介绍
如果您看到以下代码,请直接跳过,因为它并没有太大的作用:- public AnnotationConfigApplicationContext() {
- StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
- // 额外会创建StandardEnvironment
- this.reader = new AnnotatedBeanDefinitionReader(this);
- createAnnotatedBeanDefReader.end();
- this.scanner = new ClassPathBeanDefinitionScanner(this);
- }
复制代码 需要注意的是,其中的 StartupStep 关于默认实现并没有什么实际作用。但是,还有一种实现方式是 FlightRecorderStartupStep,它是JDK的JFR技术。
Bean的生成过程
生成BeanDefinition
BeanDefinition的作用大家基本通过前面的文章也知道了大概,就是用来描述bean的。
那么它是如何加载的呢?首先我们看一下 ClassPathBeanDefinitionScanner 类,它是用于扫描的。其中有一个属性是 BeanDefinitionRegistry,即Bean定义的注册类。默认实现是 DefaultListableBeanFactory,但是在 ClassPathBeanDefinitionScanner 类中并没有直接使用该类作为属性,而是使用了它的父接口 BeanDefinitionRegistry。这是因为 ClassPathBeanDefinitionScanner 类实际上并没有使用 BeanDefinitionRegistry 接口中的许多方法来注册Bean定义。
接下来,我们来分析 ClassPathBeanDefinitionScanner 类的 scan 方法:- 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) {
- // 解析@Lazy、@Primary、@DependsOn、@Role、@Description
- AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
- }
- // 检查Spring容器中是否已经存在该beanName
- 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;
- }
复制代码 大致逻辑如下:
- 获取扫描包路径
- findCandidateComponents:获取符合条件的bean
- 遍历candidate(候选bean),由于第二部使用了ASM技术,所以并没有真正获取beanclass而是使用了beanname替代所以,遍历的做法就是将符合条件的bean定义进行注册。
- scopeMetadata解析scope注解。
- beanNameGenerator构建当前bean的唯一名字。
- postProcessBeanDefinition这里其实就是进行默认值赋值。
- processCommonDefinitionAnnotations进行解析@Lazy、@Primary、@DependsOn、@Role、@Description
- checkCandidate(beanName, candidate)再次检查是否该beanName已经注册过。
- registerBeanDefinition,注册到我们的DefaultListableBeanFactory的BeanDefinitionMap中。
其实这里基本就已经大概了解 的差不多了,然后再继续讲解下每一个流程里面都走了那些逻辑:
findCandidateComponents
其主要逻辑会进入如下源码:- private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
- Set<BeanDefinition> candidates = new LinkedHashSet<>();
- try {
- // 获取basePackage下所有的文件资源
- 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);
- // excludeFilters、includeFilters判断
- if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断
- ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
- sbd.setSource(resource);
- if (isCandidateComponent(sbd)) {
- if (debugEnabled) {
- logger.debug("Identified candidate component class: " + resource);
- }
- candidates.add(sbd);
- }
- //此处省略部分代码
- ......
- }
- return candidates;
- }
复制代码 让我们来深入了解一下Bean扫描的具体细节。以下是主要流程:
- 获取basePackage下所有的文件资源。以下分析注解信息等都是用到了ASM技术,并没有真正的去加载这个类。
- isCandidateComponent(metadataReader),进行判断是否当前类具有@component注解。
- isCandidateComponent(sbd),进行判断是否当前类属于内部类、接口、抽象类
- 符合上述条件则会加入到bean定义候选集合中。
ASM技术这里不做多解释,主要看下Spring是如何进行判断校验当前bean是否符合条件的,第一个 isCandidateComponent(metadataReader)方法:- protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
- for (TypeFilter tf : this.excludeFilters) {
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- return false;
- }
- }
- // 符合includeFilters的会进行条件匹配,通过了才是Bean,也就是先看有没有@Component,再看是否符合@Conditional
- for (TypeFilter tf : this.includeFilters) {
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- return isConditionMatch(metadataReader);
- }
- }
- return false;
- }
复制代码 那么includeFilters默认会在启动AnnotationConfigApplicationContext时就会默认注册一个解析Component注解的filter,代码如下:
[code]protected void registerDefaultFilters() { // 注册@Component对应的AnnotationTypeFilter this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class |