spring boot 启动源码分析

1. spring boot 启动代码

首先, 看一段 spring boot 的启动代码如下. 发现启动分为2大部分:

  • SpringApplication.run() 容器启动
  • @SpringBootApplication 注解
// (1) 标明是 spring boot 应用, 开启自动配置
@SpringBootApplication   
public class Example {
    public static void main(String[] args) {
        // (2) SpringApplication.run() xxx启动 spring boot 应用
        ConfigurableApplicationContext context = SpringApplication.run(Example.class);
    }
}
2. 关于 SpringApplication.run() 容器启动

看到使用 SpringApplication.run(Class<?>[] primarySources) 这句话启动应用. 内部其实返回了

return new SpringApplication(primarySources).run(args);

这个方法的关键在于两点:

  1. new SpringApplication(primarySources) 构造器
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    	// resourceLoader 是 spring framework 的内容, 参看 spring framework
    	this.resourceLoader = resourceLoader;
    	Assert.notNull(primarySources, "PrimarySources must not be null");
    	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));   // java config 数组
    	//(1) 判断是否是 web 应用. 返回 (REACTIVE, NONE, SERVLET) 类型.  内部就是用 try{Class.forName()}, 看几个 class 是否存在
    	this.webApplicationType = WebApplicationType.deduceFromClasspath();
    	// (2)
    	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	// (3)
    	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    	// (4)
    	this.mainApplicationClass = deduceMainApplicationClass();
    }
    
    private ResourceLoader resourceLoader;  // spring 资源加载器
    private Set<Class<?>> primarySources;   // java config 数组
    private WebApplicationType webApplicationType;   // web 类型
    
    
    private List<ApplicationContextInitializer<?>> initializers;
    private List<ApplicationListener<?>> listeners;
    
    • (2),(3) 两步用到了 getSpringFactoriesInstances 方法来初始化字节的 initializers 属性和 listener 属性. 该方法如下:
      private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
          ClassLoader classLoader = getClassLoader();
          // 解析 'META_INF/spring.factories' 中的配置, 该文件每行是一个 key-value 对; key: 工厂类名, value: 工厂实现类的类名
          // 将 factoryName 和 fatoryImplementationName 解析出来
          Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
          // 内部用反射 constructor 构造对象
          List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
          AnnotationAwareOrderComparator.sort(instances);
          return instances;
      }
      
      因此, (2),(3)两步就是反射构造函数去实例化 META_INF/spring.factories 文件中配置的2组类 ApplicationContextInitializerApplicationListener. 文件内容为:
      # Initializers
      org.springframework.context.ApplicationContextInitializer=\
      org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
      org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
      
      # Application Listeners
      org.springframework.context.ApplicationListener=\
      org.springframework.boot.autoconfigure.BackgroundPreinitializer
      
      [说明]: META_INF/spring.factories 文件是 spring-boot 自动配置的关键
      以 mybatis 为例, 和 spring-boot 集成时, 会引入一个 mybatis-spring-boot-autoconfigure.jar 包. 最重要的是在 META_INF/spring.factories中定义了一些列工厂类, 这些类本身作为 bean, 又可以加载其它 bean. 像 mybatis 的自动配置入口就注册了 sqlSessionFactorysqlSessionTemplate:
      @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
      class MybatisAutoConfiguration implements InitializingBean{
       @Bean
       @ConditionalOnMissingBean
       public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
         return new SqlSessionTemplate(sqlSessionFactory);
       }
      }
      
    • 第(4)步: deduceMainApplicationClass() 是尝试获取执行 main 方法的类. 没什么用, 主要用来打日志
      // 通过构造一个 RuntimeException, 查找 main 方法的调用栈来决定启动类是什么
      private Class<?> deduceMainApplicationClass() {
              try {
                  StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                  for (StackTraceElement stackTraceElement : stackTrace) {
                      // 调用栈发现 main 方法, 获取主类
                      if ("main".equals(stackTraceElement.getMethodName())) {
                          return Class.forName(stackTraceElement.getClassName());
                      }
                  }
              }
              catch (ClassNotFoundException ex) {
                  // Swallow and continue
              }
              return null;
          }
      
  2. SpringApplication 的 run 方法
    该方法逻辑较多
    public ConfigurableApplicationContext run(String... args) {
        
        //StopWatch 主要用来统计 run() 方法的启动时长. 后面有掉用 stopWatch.stop() 方法
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 设置了 java.awt.headless 环境变量, 和 awt 有关, 可以无视
        configureHeadlessProperty();
        
        // 反射构造器, 实例化 spring.factories 中的 SpringApplicationRunListener 实现类并启动
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            // (1) prepareEnvironment 加载配置变量. 该方法, 根据内部会根据 spring-boot 配置的 profile 加载配置.
            //     生成 PropertySource 和 Environment. 这两个类时干什么的, 参见 spring-framework
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            
            // (2) 打印 banner
            Banner printedBanner = printBanner(environment);
            // (3) 创建 ApplicaitonContext. 详见下方
            context = createApplicationContext();
            // (4) 对 spring.factories 中记录的配置的 SpringBootExceptionReporter 实现类
            //     org.springframework.boot.diagnostics.FailureAnalyzers 做初始化
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            // (5) 见下面
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // (6) 内部就是替我们手动执行了 applicationContext.refresh()
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            // 同下(8)
            listeners.started(context);
            // (7) 执行所有类型为 `ApplicationRunner` 和 `CommandLineRunner` 的 bean 的 run() 方法
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
            // (8) 有关 SpringApplicationRunListeners 
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
    
  • (1)处, prepareEnvironment 加载配置变量
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // Create and configure the environment
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            ConfigurationPropertySources.attach(environment);
            listeners.environmentPrepared(environment);
            bindToSpringApplication(environment);
            if (!this.isCustomEnvironment) {
                environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                        deduceEnvironmentClass());
            }
            ConfigurationPropertySources.attach(environment);
            return environment;
        }
    
  • (2)处, 打印的 banner 如下
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::
    
  • (3) createApplicationContext 创建 ConfigurableApplicationContext
    该方法根据判断出的 web 类型(普通web, webserver等)通过反射构造器, 创建 ApplicationContext 对象. 相当于帮你手写了 new AnnotationConfigApplicationContext() 初始化容器
    protected ConfigurableApplicationContext createApplicationContext() {
        // 根据 webApplicationType 类型,获得 ApplicationContext 类型
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            } catch (ClassNotFoundException ex) {
                throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
            }
        }
        // 创建 ApplicationContext 对象
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    
  • (5) prepareContext 准备 ApplicationContext 对象,主要是初始化它的一些属性。
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // (1) 给 applicationContext 设置配的变量
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        // (2) 缓存 ApplicationContextInitializer
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //(3) 在 applicationContexdt 的 BeanFactory 中缓存启动参数. 名称为 "springApplicationArguments"
        //    内部是缓存在 SingletonBeanRegistry 的 LinkedHashSet 数据结构中
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
    
        // (4) 加载一系列的 BeanDefinition. 具体如下方法
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }
    // prepareContext() 的第(4)步骤: 分析怎么加载的 BeanDefinition
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        // (1) 创建 BeanDefinitionRegistry 对象. 具体什么是 BeanDefinitionRegistry 和 BeanDefinition, 参见 spring framework
        BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        // 执行 BeanDefinition 加载. 具体加载步骤参见 spring-framework
        loader.load();
    }
    
  • (8) 是 SpringApplicationRunListeners 相关处理, 包括 started()run() 方法. 参看事件设计模式
3. @SpringBootApplication 注解开启自动配置

自动配置和自动装配不同. 自动装配指 spring bean 的生成, 自动配置是 spring-boot 的主要动能. 首先先看看该注解的定义

@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication 

下面逐步分析 @SpringBootApplication 上的每个注解

  1. @Inherited
    该注解是 java 自带注解, 其含义是: 当用 @Inherited 实现了自定义注解, 如果在 class 上使用自定义注解, 则继承该 class 的子类自动被标记了自定义注解

  2. @SpringBootConfiguration
    spring boot 的注解. 看到定义时用到了 @Configuration, 因此两者含义一致, 表示该类作为 beans.xml 的替代者来声明 bean

    @Configuration
    public @interface SpringBootConfiguration {}
    
  3. @ComponentScan
    spring 注解, 自动扫描包下@Componment@Configuration@Service

  4. @EnableAutoConfiguration
    spring boot 实现自动配置的核心注解

    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
    • @AutoConfigurationPackage 注解

      @Import(AutoConfigurationPackages.Registrar.class)
      public @interface AutoConfigurationPackage
      
    • @Import

      1. 该注解的功能

        • (1) @Import(OtherConfiguration.class): 导入其它 @Configuration 声明的类, 相当于 beans.xml 中 <import> 标签的代替
        • (2) @Import(OtherImportSelector.class): 导入 ImportSelector 中代码定义的类名
          package com.test;
          class ServiceImportSelector implements ImportSelector {
              @Override
              public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                  //可以是@Configuration注解修饰的类,也可以是具体的Bean类的全限定名称
                  return new String[]{"com.test.ConfigB"};
              }
          }
          
          @Import(ServiceImportSelector.class)
          @Configuration
          class ConfigA {
              @Bean
              @ConditionalOnMissingBean
              public ServiceInterface getServiceA() {
                  return new ServiceA();
              }
          }
          
        • (3) @Import(ImportBeanDefinitionRegistrar.class): 用于在导入 bean 时, 重新定义或更改 bean. 例如动态注入属性,改变Bean的类型和Scope等等
          // 定义ServiceC
          package com.test;
          class ServiceC implements ServiceInterface {
          
              private final String name;
          
              ServiceC(String name) {
                  this.name = name;
              }
          
              @Override
              public void test() {
                  System.out.println(name);
              }
          }
          
          // 定义ServiceImportBeanDefinitionRegistrar动态注册ServiceC,修改EnableService
          package com.test;
          
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Target(ElementType.TYPE)
          @Import(ServiceImportBeanDefinitionRegistrar.class)
          @interface EnableService {
              String name();
          }
          
          class ServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
              @Override
              public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                  Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
                  String name = (String) map.get("name");
                  BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class)
                          //增加构造参数
                          .addConstructorArgValue(name);
                  //注册Bean
                  registry.registerBeanDefinition("serviceC", beanDefinitionBuilder.getBeanDefinition());
              }
          }
          
          // 使用 @Import 注解 
          package com.test;
          @EnableService(name = "TestServiceC")
          @Configuration
          class ConfigA {}     // 会自动生成
          
          // main 函数 
          public static void main(String[] args) {
              ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);
              ServiceInterface bean = ctx.getBean(ServiceInterface.class);
              bean.test();
          }
          

        [注]: @Import 注解的解析, 参考 https://zhuanlan.zhihu.com/p/147025312

      2. @Import(AutoConfigurationImportSelector.class) 是重头戏的开始, 如后文

4. @Import(AutoConfigurationImportSelector.class) 自动配置重头戏

AutoConfigurationImportSelector 类定义如下. 可以发现它除了实现几个Aware类接口外,最关键的就是实现了 DeferredImportSelector (继承自ImportSelector)接口 https://blog.csdn.net/dm_vincent/article/details/77619752

class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{

}
  1. 该类中的 getCandidateConfigurations 获取配置类数组

    1. 函数展示

      // AutoConfigurationImportSelector.java
      
      protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
          // <1> 加载指定类型 EnableAutoConfiguration 对应的,在 `META-INF/spring.factories` 里的类名的数组
          // getSpringFactoriesLoaderFactoryClass(): return EnableAutoConfiguration.class;
          // SpringFactoriesLoader.loadFactoryNames():  在 `META-INF/spring.factories` 里的类名的数组
      	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
      	return configurations;
      }
      
    2. getCandidateConfigurations 方法如何被调用的

      • (1)处: 在上述分析 .run() 方法时, 第(6)步, 替我们手动执行了 applicationContext.refresh()
      • (3)处:
      public Iterable<Group.Entry> getImports() {
      	for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
      		// <1> 处理被 @Import 注解的注解
      		// this.group 由 getImportGroup() 方法返回, 具体在"该类的 `getImportGroup()`"章节分析
      		this.group.process(deferredImport.getConfigurationClass().getMetadata(),
      				deferredImport.getImportSelector());
      	}
      	// <2> 选择需要导入的
      	return this.group.selectImports();
      }
      
  2. 该类的 getImportGroup()

    1. 函数展示
    // AutoConfigurationImportSelector.java
    
    @Override    // 实现自 DeferredImportSelector 接口
    public Class<? extends Group> getImportGroup() {
    	return AutoConfigurationGroup.class;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值