Springboot启动后,BeanDefinition的生成过程

目录

前言

大家好,我是oxye马儿,最近研究了Springboot的启动流程,非常长,bean加载流程在其中只是一块拼图。然后我就看bean生成流程,总结的过程中,感觉对于Bean生成的流程倒是看了,但是BeanDefinition怎么来的不知道哇,不知道来源只知道使用 那指定不行,所以再看一下BeanDefinition bean定义的生成流程

看的时候发现BeanDefinition和一些Bean一样,不同种类的是在不同阶段生成的,比如

1.框架硬编码的,启动时,先会根据源码中写死的类路径加载一些bean

2.开发自己写的,后面再生成我们平时使用的@Component、@Controller、@Service等注解的bean

3.实现一些Spring源码的接口来加载自定义bean,比如Mybatis就实现了ImportBeanDefinitionRegistrar

等等

这篇先写一下第二种BeanDefinition的生成过程,看看我们开发中注解的Bean是怎么被找到,生成BeanDefinition的

Springboot版本:2.3.2

代码流程

1.JaylinApplication::main (com.jaylin.jaylin)

启动类main函数

package com.jaylin.jaylin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JaylinApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(JaylinApplication.class, args);
    }
}

2.SpringApplication::run (org.springframework.boot)

SpringApplication的第一个静态方法run,调用第二个静态方法run

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    
    
   return run(new Class<?>[] {
    
     primarySource }, args);
}

3.SpringApplication::run (org.springframework.boot)

SpringApplication的第二个静态方法run,创建一个SpringApplication实例(会做赋予启动类、推算webApplicationType、设置启动器、设置监听器等操作),并调用非静态方法,第三个run

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    
    
   return new SpringApplication(primarySources).run(args);
}

4.SpringApplication::run (org.springframework.boot)

SpringApplication的第三个静态方法run(会做获取监听器、触发一些事件、准备环境、打印banner、创建应用上下文、准备应用上下文、刷新应用上下文等操作)

部分操作↓

//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//打印启动团
Banner printedBanner = printBanner(environment);
//创建应用上下文
context = createApplicationContext();
//准备应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新应用上下文
refreshContext(context);
//后续操作
afterRefresh(context, applicationArguments);

5.SpringApplication::refreshContext (org.springframework.boot)

刷新应用上下文

private void refreshContext(ConfigurableApplicationContext context) {
    
    
   refresh((ApplicationContext) context);
   if (this.registerShutdownHook) {
    
    
      try {
    
    
         context.registerShutdownHook();
      }
      catch (AccessControlException ex) {
    
    
         // Not allowed in some environments.
      }
   }
}

6.SpringApplication::refresh (org.springframework.boot)

SpringApplication的第一个静态方法refresh,只是调用第二个静态方法refresh,将参数向下转型ApplicationContext -> ConfigurableApplicationContext

protected void refresh(ApplicationContext applicationContext) {
    
    
   Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
   refresh((ConfigurableApplicationContext) applicationContext);
}

7.SpringApplication::refresh (org.springframework.boot)

SpringApplication的第二个静态方法fresh,调用了入参的refresh方法,实现ConfigurableApplicationContext的refresh的有三个类,这里入参context在前面进行判断时,根据webApplicationType,AnnotationConfigServletWebServerApplicationContext,此类又直接继承了ServletWebServerApplicationContext

protected void refresh(ConfigurableApplicationContext applicationContext) {
    
    
   applicationContext.refresh();
}

8.ServletWebServerApplicationContext::refresh (org.springframework.boot.web.servlet.context)

ServletWebServerApplicationContext的refresh直接super.refresh,调用了他先祖AbstractApplicationContext的refresh,好处是添加了异常处理逻辑

@Override
public final void refresh() throws BeansException, IllegalStateException {
    
    
   try {
    
    
      super.refresh();
   }
   catch (RuntimeException ex) {
    
    
      WebServer webServer = this.webServer;
      if (webServer != null) {
    
    
         webServer.stop();
      }
      throw ex;
   }
}

9.AbstractApplicationContext::refresh (org.springframework.context.support)

AbstractApplicationContext的refresh方法,又关键又长(会做刷新准备、准备BeanFactory、设置钩子方法、调用已注册的BeanFactoryPostProcessor、注册、初始化信息源、注册监听器、初始化各种类型bean等操作)

10.AbstractApplicationContext::invokeBeanFactoryPostProcessors (org.springframework.context.support)

实例化和调用所有BeanFactoryPostProcessor(包括其子类 BeanDefinitionRegistryPostProcessor),BeanFactoryPostProcessor接口是 Spring初始化BeanFactory 时对外暴露的扩展点,Spring ioc 容器允许 BeanFactoryPostProcessor在容器实例化任何bean之前,读取并修改BeanDefinition

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    
    
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

   // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
   if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    
    
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }
}

11.PostProcessorRegistrationDelegate::invokeBeanFactoryPostProcessors (org.springframework.context.support)

PostProcessorRegistrationDelegate的静态方法,方法名称同上一步一样

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

12.PostProcessorRegistrationDelegate::invokeBeanDefinitionRegistryPostProcessors (org.springframework.context.support)

遍历调用给定的 BeanDefinitionRegistryPostProcessor Beans,这里是ConfigurationClassPostProcessor

private static void invokeBeanDefinitionRegistryPostProcessors(
      Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
    
    

   for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
    
    
      postProcessor.postProcessBeanDefinitionRegistry(registry);
   }
}

13.ConfigurationClassPostProcessor::postProcessBeanDefinitionRegistry (org.springframework.context.annotation)

从注册表中的配置类 派生更多的BeanDefinition

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    
    
   int registryId = System.identityHashCode(registry);
   if (this.registriesPostProcessed.contains(registryId)) {
    
    
      throw new IllegalStateException(
            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
   }
   if (this.factoriesPostProcessed.contains(registryId)) {
    
    
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + registry);
   }
   this.registriesPostProcessed.add(registryId);

   processConfigBeanDefinitions(registry);
}

14.ConfigurationClassPostProcessor::processConfigBeanDefinitions (org.springframework.context.annotation)

建立并验证基于注册表的配置模型

15.ConfigurationClassParser::parse (org.springframework.context.annotation)

解析方法,入参包装了启动类,其类型是AnnotatedBeanDefinition

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    
    
   for (BeanDefinitionHolder holder : configCandidates) {
    
    
      BeanDefinition bd = holder.getBeanDefinition();
      try {
    
    
         if (bd instanceof AnnotatedBeanDefinition) {
    
    
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
         }
         else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
    
    
            parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
         }
         else {
    
    
            parse(bd.getBeanClassName(), holder.getBeanName());
         }
      }
   }
}

16.ConfigurationClassParser::parse (org.springframework.context.annotation)

专门处理注解类型AnnotatedBeanDefinition的解析方法

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    
    
   processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

17.ConfigurationClassParser::processConfigurationClass (org.springframework.context.annotation)

处理配置类

sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);

18.ConfigurationClassParser::doProcessConfigurationClass (org.springframework.context.annotation)

构建配置类,对@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean等注解 注解过的代码进行,此时因为启动类上的注解@SpringBootApplication中包含@ComponentScan,所以走到了@ComponentScan对应的逻辑

protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {
    
    

   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
    
    
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass, filter);
   }

   // Process any @PropertySource annotations
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
    
    
      if (this.environment instanceof ConfigurableEnvironment) {
    
    
         processPropertySource(propertySource);
      }
      else {
    
    
         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // Process any @ComponentScan annotations
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    
    
      for (AnnotationAttributes componentScan : componentScans) {
    
    
         // The config class is annotated with @ComponentScan -> perform the scan immediately
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
    
    
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
    
    
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
    
    
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // Process any @Import annotations
   processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

   // Process any @ImportResource annotations
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if (importResource != null) {
    
    
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
    
    
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }

   // Process individual @Bean methods
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
    
    
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // Process default methods on interfaces
   processInterfaces(configClass, sourceClass);

   // Process superclass, if any
   if (sourceClass.getMetadata().hasSuperClass()) {
    
    
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
    
    
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}

19.ComponentScanAnnotationParser::parse (org.springframework.context.annotation)

配置一个类路径扫描器ClassPathBeanDefinitionScanner,把入参中的值赋予它,比如扫描路径resourcePattern值为**/*.class,等等属性,然后开始扫描

return scanner.doScan(StringUtils.toStringArray(basePackages));

20.ClassPathBeanDefinitionScanner::doScan (org.springframework.context.annotation)

在指定的路径下执行扫描,返回注册的bean定义,我们的路径就是启动类所在路径(Packages下)。这里会根据启动类路径(classpath*:com/jaylin/jaylin/**/*.class),使用scanCandidateComponents,扫描到所有class文件到resources数组,然后经过筛选得到一个候选者大军candidates(Set类型),然后依次遍历这些候选者,构造BeanDefinitionHolder,逐个执行registerBeanDefinition

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

21.ClassPathBeanDefinitionScanner::registerBeanDefinition (org.springframework.context.annotation)

使用给定的注册表注册指定的bean

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    
    
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

22.BeanDefinitionReaderUtils::registerBeanDefinition (org.springframework.beans.factory.support)

将beanName和definitionHolder中的BeanDefinition交给下一步,registry是DefaultListableBeanFactory

// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

23.DefaultListableBeanFactory::registerBeanDefinition (org.springframework.beans.factory.support)

经过校验后,新增一个新的BeanDefinition(this.beanDefinitionMap.put(beanName, beanDefinition);)

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

过程中的问题

注解怎么扫描到的

第20步中

findCandidateComponents中,获取注解列表,最后生成候选者们

获取注解类

​ scanCandidateComponents -> getMetadataReader -> CachingMetadataReaderFactory::getMetadataReader -> new SimpleMetadataReader

判断注解是否合适

​ isCandidateComponent中excludeFilters和includeFilters,启动类注解中只有excludeFilters,但是在Springboot项目启动后,默认应用上下文是AnnotationConfigServletWebServerApplicationContext,然后在SpringApplication::createApplicationContext后续操作中,走到ClassPathScanningCandidateComponentProvider::registerDefaultFilters,第一行就是this.includeFilters.add(new AnnotationTypeFilter(Component.class));
​ 然后呢,@Controller,@Service和@Repository等常见注解,点进去看源码,他们也都注解了**@Component**,所以有这些注解的类才能被加入候选者
​ @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
​ @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

beanName怎么来的

第20步 doscan中

generateBeanName中,会先尝试获取注解中配置的名称,如果没有,就获取类名,首位小写

猜你喜欢

转载自blog.csdn.net/weixin_43859729/article/details/108897270