美文网首页
2021-02-28_SpringBoot源码学习笔记之启动流程

2021-02-28_SpringBoot源码学习笔记之启动流程

作者: kikop | 来源:发表于2021-03-02 06:41 被阅读0次

SpringBoot源码学习笔记之启动流程分析

1概述

本文基于Spring Boot版本2.1.4(SringBoot最新版本2.4.2。(参考文档: https://docs.spring.io/spring-boot/docs/current/api/)

2SpringBoot启动流程分析

2.1分析入口

@SpringBootApplication
public class DemoApplication {

   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }

}
// spring-boot-2.1.4.RELEASE-sources.jar!\org\springframework\boot\SpringApplication.java
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   //1.选择ApplicationContentType容器类型
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //2.初始化,得到 ApplicationContextInitializer.class的springFactories
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   //3.设置监听器 
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //4.设置主类(有main的那个类) 
   this.mainApplicationClass = deduceMainApplicationClass();
}

2.2容器选择deduceFromClasspath

/**
 * An enumeration of possible types of web application.
 *
 * @author Andy Wilkinson
 * @author Brian Clozel
 * @since 2.0.0
 */
public enum WebApplicationType {

   /**
    * The application should not run as a web application and should not start an
    * embedded web server.
    */
   NONE,

   /**
    * The application should run as a servlet-based web application and should start an
    * embedded servlet web server.
    */
   SERVLET,

   /**
    * The application should run as a reactive web application and should start an
    * embedded reactive web server.
    */
   REACTIVE;

2.2setInitializers(ApplicationContextInitializer)

0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
3 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
4 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
5 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"

2.3setListeners(ApplicationListener)

0 = "org.springframework.boot.ClearCachesApplicationListener"
1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener"
2 = "org.springframework.boot.context.FileEncodingApplicationListener"
3 = "org.springframework.boot.context.config.AnsiOutputApplicationListener"
4 = "org.springframework.boot.context.config.ConfigFileApplicationListener"
5 = "org.springframework.boot.context.config.DelegatingApplicationListener"
6 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener"
7 = "org.springframework.boot.context.logging.LoggingApplicationListener"
8 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"
9 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"

2.5run

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   // 1.获取 spring:SpringApplicationRunListener 
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      // 2.这里涉及容器的创建、刷新等,类似于Spring
      // org.springframework.boot.web.servlet.context.
      //AnnotationConfigServletWebServerApplicationContext@25ce9dc4, started on Thu Jan 01 08:00:00 CST 1970 
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

3源码细节

3.1getSpringFactoriesInstances

// spring-boot-2.1.4.RELEASE-sources.jar!\org\springframework\boot\SpringApplication.java
//根据 type和 classLoader得到springFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   // 1.根据 ApplicationContextInitializer 
   Set<String> names = new LinkedHashSet<>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   // 2.springFactories实例化
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
   // 3.排序 
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}
// spring-core-5.1.6.RELEASE-sources.jar!\org\springframework\core\io\support\SpringFactoriesLoader.java
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
   // 1.得到 org.springframework.context.ApplicationContextInitializer:ConfigurableApplicationContext
   String factoryClassName = factoryClass.getName();
   return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
   // 0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
   // 1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
   // 2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
   // 3 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
   // 4 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
   // 5 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
}
// spring-core-5.1.6.RELEASE-sources.jar!\org\springframework\core\io\support\SpringFactoriesLoader.java

    /**
     * The location to look for factories.
     * <p>Can be present in multiple JAR files.
     */
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   // todo,cache数据来源 
   // 启动的时候会扫描所有jar包下META-INF/spring.factories这个文件。第二段代码的意思是将这些扫描到的文件转成Properties
   // 对象,后面两个核心代码的意思就是说将加载到的Properties对象放入到缓存中
    
   MultiValueMap<String, String> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   try {
      // 加载路径       
      Enumeration<URL> urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryClassName = ((String) entry.getKey()).trim();
            for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
               result.add(factoryClassName, factoryName.trim());
            }
         }
      }
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

3.2启动类上的注解

/**
 * Indicates a {@link Configuration configuration} class that declares one or more
 * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
 * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
 * annotation that is equivalent to declaring {@code @Configuration},
 * {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
 *
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @since 1.2.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//1.@SpringBootConfiguration这个注解说明再点进去查看详情发现就是一个@Configuration注解,这说明启动类就是一个配置类。支持Spring以JavaConfig的形式启动。
@SpringBootConfiguration
//3.springboot的核心注解
@EnableAutoConfiguration
//2.组件扫描的意思,即默认扫描当前package以及其子包下面的spring的注解,例如:@Controller、@Service、@Component等等注解。
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM,
            classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

   /**
    * Exclude specific auto-configuration classes such that they will never be applied.
    * @return the classes to exclude
    */
   @AliasFor(annotation = EnableAutoConfiguration.class)
   Class<?>[] exclude() default {};

   /**
    * Exclude specific auto-configuration class names such that they will never be
    * applied.
    * @return the class names to exclude
    * @since 1.3.0
    */
   @AliasFor(annotation = EnableAutoConfiguration.class)
   String[] excludeName() default {};

   /**
    * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
    * for a type-safe alternative to String-based package names.
    * @return base packages to scan
    * @since 1.3.0
    */
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};

   /**
    * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
    * scan for annotated components. The package of each class specified will be scanned.
    * <p>
    * Consider creating a special no-op marker class or interface in each package that
    * serves no purpose other than being referenced by this attribute.
    * @return base packages to scan
    * @since 1.3.0
    */
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};

}

3.2.1SpringBootConfiguration

3.2.2ComponentScan

3.2.3EnableAutoConfiguration

该注解是springboot的核心注解,本质都是利用了spring framework的 import功能。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//1.
@AutoConfigurationPackage
//2.
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

   /**
    * Exclude specific auto-configuration classes such that they will never be applied.
    * @return the classes to exclude
    */
   Class<?>[] exclude() default {};

   /**
    * Exclude specific auto-configuration class names such that they will never be
    * applied.
    * @return the class names to exclude
    * @since 1.3.0
    */
   String[] excludeName() default {};

}

3.2.3.1AutoConfigurationPackage

自动配置包,最终依赖的确实@Import这个注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 后置处理器,将BeanDef注入到ioc容器
// 导入类型为:Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

3.2.3.2AutoConfigurationImportSelector

自动引入组件

/**
 * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
 * auto-configuration}. This class can also be subclassed if a custom variant of
 * {@link EnableAutoConfiguration @EnableAutoConfiguration} is needed.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @author Stephane Nicoll
 * @author Madhura Bhave
 * @since 1.3.0
 * @see EnableAutoConfiguration
 */
public class AutoConfigurationImportSelector
      implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
      BeanFactoryAware, EnvironmentAware, Ordered {

   private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();

   private static final String[] NO_IMPORTS = {};

   private static final Log logger = LogFactory
         .getLog(AutoConfigurationImportSelector.class);

   private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

   private ConfigurableListableBeanFactory beanFactory;

   private Environment environment;

   private ClassLoader beanClassLoader;

   private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
         autoConfigurationMetadata, annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
final class AutoConfigurationMetadataLoader {

   protected static final String PATH = "META-INF/"
         + "spring-autoconfigure-metadata.properties";

   private AutoConfigurationMetadataLoader() {
   }

   public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
      return loadMetadata(classLoader, PATH);
   }
// spring-boot-autoconfigure-2.1.4.RELEASE-sources.jar!/
// org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
// 执行时机:refresh(AbstractApplicationContext.java):invokeBeanFactoryPostProcessors(beanFactory);
                
/*
spring-boot-autoconfigure\2.1.4.RELEASE\spring-boot-autoconfigure-2.1.4.RELEASE.jar!\
META-INF\spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
*/  

/**
 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 * of the importing {@link Configuration @Configuration} class.
 * @param autoConfigurationMetadata the auto-configuration metadata
 * @param annotationMetadata the annotation metadata of the configuration class
 * @return the auto-configurations that should be imported
 */
protected AutoConfigurationEntry getAutoConfigurationEntry(
      AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 这就是一种自定义SPI的实现方式的功能
    // 获取所有jar中类型为:AutoConfiguration.class的子类,例如:
    // 0 = "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration"
    // 1 = "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration"
    // 2 = "org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration"
   List<String> configurations = getCandidateConfigurations(annotationMetadata,
         attributes);
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}
/**
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {@link SpringFactoriesLoader} with
 * {@link #getSpringFactoriesLoaderFactoryClass()}.
 * @param metadata the source metadata
 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
 * attributes}
 * @return a list of candidate configurations
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
      AnnotationAttributes attributes) {
   // 加载路径:META-INF/spring.factories 
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
         getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
   Assert.notEmpty(configurations,
         "No auto configuration classes found in META-INF/spring.factories. If you "
               + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}
@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class})
// 条件注解,加载SpringApplicationAdminJmxAutoConfiguration的bean条件
@ConditionalOnProperty(
    prefix = "spring.application.admin",
    value = {"enabled"},
    havingValue = "true",
    matchIfMissing = false
)
public class SpringApplicationAdminJmxAutoConfiguration {
@Configuration
@ConditionalOnClass({ MBeanExporter.class })
@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true",
      matchIfMissing = true)
public class JmxAutoConfiguration implements EnvironmentAware, BeanFactoryAware {

4总结

  1. SpringBoot在启动的时候,首先会构造SpringApplication对象,获取并设置Initializers和Listeners,得到main启动类。

  2. 接着调用run()方法,run()方法执行容器刷新,

    2.1.刷新容器的时候,执行到容器的后置处理器invokeBeanFactoryPostProcessors方法时,会去解析启动类上的@SpringBootApplication复合注解。

    2.2.这个注解中有一个@EnableAutoConfiguration注解,表示开启自动配置功能(实际是import :AutoConfigurationPackages.Registrar.class)

    2.3.另外还有个@Import注解引入了一个AutoConfigurationImportSelector这个类(它去扫描我们所有jar包下的META-INF下的spring.factories 自动配置类文件,而从这个配置文件中取找key为EnableAutoConfiguration类的全路径的值,并加载到容器)

  3. 然后在根据这些配置类中的条件注解,来判断是否将这些配置类及关联的Bean在容器中进行实例化,

  4. (判断的条件:主要是判断项目是否有相关jar包或工程是否引入了相关的bean)。

参考

1.springboot启动时的一个自动装配过程

https://www.jianshu.com/p/c50b58b03c60

相关文章

网友评论

      本文标题:2021-02-28_SpringBoot源码学习笔记之启动流程

      本文链接:https://www.haomeiwen.com/subject/ausofltx.html