zoukankan      html  css  js  c++  java
  • Spring Boot 启动机制源码阅读(粗略)

    【写在阅读前和写作后。总体来说,对于这次阅读效果不是很满意。没有梳理清楚流程和逻辑,只是通读了代码。而且对于最关键的几个实现,类加载、Listener、Processor几个关键方法都没有解读的很清楚。私以为要搞清楚这些,需要对于Spring的实现思路有一定了解,另外需要了解一下相关的设计模式才好解读。这几天再梳理和继续阅读,后面再出一篇】

     

    理清主线再深入细节

    main方法解析

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration //是一个配置类
    @EnableAutoConfiguration //开启自动配置?-》什么原理
    //开启默认的扫描,也就是说扫描当前路径是通过这里达成的
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {

     

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    //分成两部分:1.生成应用对象 2.执行run方法
      return new SpringApplication(primarySources).run(args);
    }

     

    new SpringApplication(primarySources)部分

    /**
    * Create a new {@link SpringApplication} instance. The application context will load
    * beans from the specified primary sources (see {@link SpringApplication class-level}
    * documentation for details. The instance can be customized before calling
    * {@link #run(String...)}.
    * @param resourceLoader the resource loader to use
    * @param primarySources the primary bean sources
    * @see #run(Class, String[])
    * @see #setSources(Set)
    */
    @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));
      //貌似是在识别应用类型,此处默认的结果为SERVLET
      this.webApplicationType = WebApplicationType.deduceFromClasspath();
      //这部分核心为getSpringFactoriesInstances,主要做了三件事
      //1.从META/spring.facteries文件中读取预定义的ApplicationContextInitializer的相关实现全路径类名
      //2.把读取到的类实例化
      //3.根据javax.annotation.Priority的值排序(值大的优先级高)
      setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
       //同上,这部分是读取META/spring.facteries下的ApplicationListener相关配置,实例化,排序
      setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
       //通过运行堆栈找出main方法所在的主类
       //这里有一个好玩的地方是,通过 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); 是可以拿到当前的执行堆栈,回溯前文的
      this.mainApplicationClass = deduceMainApplicationClass();
    }

     

    加载细节就只解读到这个层次,后续研究类加载器文件加载时,可以以这部分为参考来学习。

    这部分细节涉及的关键点主要是两个,一个是类加载器,一个是缓存。对于后续如果要解读整体的缓存方案,这部分也需要了解。

    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.从META/spring.facteries文件中读取预定义的ApplicationContextInitializer的相关实现全路径类名
      Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
       //2.把读取到的类实例化
      List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
       //3.根据javax.annotation.Priority的值排序(值大的优先级高)
      AnnotationAwareOrderComparator.sort(instances);
      return instances;
    }

     

     

    最基础的Spring-Boot-Web的项目,

    默认加载的ApplicationContextInitializer为:(排序后)

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

    默认加载的ApplicationListener为:(排序后)

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

    默认执行到这里的堆栈为:

    0 = {StackTraceElement@1968} "org.springframework.boot.SpringApplication.deduceMainApplicationClass(SpringApplication.java:279)" 1 = {StackTraceElement@1969} "org.springframework.boot.SpringApplication.<init>(SpringApplication.java:274)" 2 = {StackTraceElement@1970} "org.springframework.boot.SpringApplication.<init>(SpringApplication.java:253)" 3 = {StackTraceElement@1971} "org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)" 4 = {StackTraceElement@1972} "org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)" 5 = {StackTraceElement@1973} "com.example.demo.DemoApplication.main(DemoApplication.java:10)"

     

    SpringApplication.run()方法部分

    主体代码为:

    /**
    * 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 stopWatch = new StopWatch();
    stopWatch.start();
           //声明上下文容器
    ConfigurableApplicationContext context = null;
           //声明异常报告容器
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
           //设置java.awt.headless为SpringApplicationheadless属性值,默认为true
    configureHeadlessProperty();
           //从META/spring.facteries中读取SpringApplicationRunListener并实例化,封装成一个SpringApplicationRunListeners对象,该对象与SpringApplication共用一个logger
    SpringApplicationRunListeners listeners = getRunListeners(args);
           //启动运行时监听器,此处只有org.springframework.boot.context.event.EventPublishingRunListener一个监听
           //这个监听做的事情如下:
           //1.新建一个org.springframework.boot.context.event.ApplicationStartingEvent事件
           //2.广播该事件(广播的原理是,拿到所有注册对于该事件感兴趣的监听,用taskExecutor执行)
           //那么问题来了,是在哪里注册的事件关注呢(追了一下源码,监听器本身常量定义了关注的事件,是在getApplicationListeners的过程中,扫描所有监听器,通过代理方法判断是否关注,然后缓存起来的)
    listeners.starting();
    try {
               //声明入参对象
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
               //准备环境,做的事情主要如下:(参见下面的解读)
               //组建一个环境对象
               //加载各种Converter、Formatter、数据读取源、profile;
               //这个过程中会广播一个ApplicationEnvironmentPreparedEvent事件
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
               //如果环境属性spring.beaninfo.ignore为空,则设置一个环境属性spring.beaninfo.ignore=true
    configureIgnoreBeanInfo(environment);
               //打印Banner信息
               //SpringApplication.bannerMode=OFF时可以关闭Banner打印
               //通过属性spring.banner.image.location读取Banner的图像信息,支持gif/jpg/png三个格式,找banner.gif、banner.jpg、banner.png
               //文字版找spring.banner.location下的banner.txt
               //图片和文字都找不到时以SpringBootBanner作为默认的banner
               //找到多个Banner时会都打印
               //打印完Banner后,会通过SpringBootVersion.getVersion()打印版本信息(此处是带颜色的打印)
    Banner printedBanner = printBanner(environment);
               //SERVLET应用,默认创建一个org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext容器,该容器创建时会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
    context = createApplicationContext();
               //从MEAT/spring.factories中加载SpringBootExceptionReporter的相关配置并创建实例
               //默认加载的只有一个 org.springframework.boot.diagnostics.FailureAnalyzers
    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    new Class[] { ConfigurableApplicationContext.class }, context);
               //初始化context容器对象(塞各种属性),
               //找到之前加载的ApplicationContextInitializer,执行initialize方法 !!!
               //并发布一个ApplicationContextInitializedEvent事件
               //由logStartupInfo开关控制,打印一条开始的info日志,如果开启了debug级别还会有一条debug的运行日志;打印激活的profile
               //注册一个springApplicationArguments的单例bean
               //printedBanner如果不为空,也注册为单例bean
               //如果是默认的bean工程DefaultListableBeanFactory,设置由allowBeanDefinitionOverriding属性控制是否允许重载
               //由lazyInitialization控制加载一个LazyInitializationBeanFactoryPostProcessor处理器
               //创建BeanDefinitionLoader,执行其load方法
               //load方法里,以Competent的方式注册入口类(这部分其他内容没有看出来意图是什么)
               //load完毕后,创建并发布一个ApplicationPreparedEvent事件
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
               //这部分具体看后面关于refresh的解析 !!!
    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;
    }

    一个Spring-Boot-Web项目

    默认加载的SpringApplicationRunListener为

    org.springframework.boot.context.event.EventPublishingRunListener

    默认关注启动事件org.springframework.boot.context.event.ApplicationStartingEvent的监听有:

    0 = {LoggingApplicationListener@2162} 1 = {BackgroundPreinitializer@2261} 2 = {DelegatingApplicationListener@2378} 3 = {LiquibaseServiceLocatorApplicationListener@2379}

     

    准备环境的代码如下:

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
         ApplicationArguments applicationArguments) {
      // Create and configure the environment
       //初始化一个环境容器
       //1.读取默认的profile
       //2.读取默认的属性读取源
      ConfigurableEnvironment environment = getOrCreateEnvironment();
       //配置环境,这里就是加载各种Converter、Formatter、配置读取属性源、profile;具体可看后面的解读
      configureEnvironment(environment, applicationArguments.getSourceArgs());
       //这里是把configurationProperties属性源放到第一个,如果没有就直接添加到第一个
      ConfigurationPropertySources.attach(environment);
       //新建并广播一个ApplicationEnvironmentPreparedEvent事件,通知关注的监听器
      listeners.environmentPrepared(environment);
       //没看到这里在干什么,注释是说把环境对象绑定到SpringApplication上
      bindToSpringApplication(environment);
      if (!this.isCustomEnvironment) {
          //如果需要转化就转换环境对象(此处默认是没有转换的)
         environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
               deduceEnvironmentClass());
      }
       //和上面一样,重新把configurationProperties属性源放到第一个
      ConfigurationPropertySources.attach(environment);
      return environment;
    }

    默认profile

    default

     

    默认的属性读取源

    0 = {PropertySource$StubPropertySource@2303} "StubPropertySource {name='servletConfigInitParams'}" 1 = {PropertySource$StubPropertySource@2368} "StubPropertySource {name='servletContextInitParams'}" 2 = {PropertiesPropertySource@2408} "PropertiesPropertySource {name='systemProperties'}" 3 = {SystemEnvironmentPropertySource@2409} "SystemEnvironmentPropertySource {name='systemEnvironment'}"

     

    配置环境代码如下:

    /**
    * Template method delegating to
    * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
    * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
    * Override this method for complete control over Environment customization, or one of
    * the above for fine-grained control over property sources or profiles, respectively.
    * @param environment this application's environment
    * @param args arguments passed to the {@code run} method
    * @see #configureProfiles(ConfigurableEnvironment, String[])
    * @see #configurePropertySources(ConfigurableEnvironment, String[])
    */
    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
      if (this.addConversionService) { //默认为true
          //获取转化服务
         ConversionService conversionService = ApplicationConversionService.getSharedInstance();
         environment.setConversionService((ConfigurableConversionService) conversionService);
      }
       //配置属性源(就是之前准备环境时读取到的),这里只是放到环境对象里
      configurePropertySources(environment, args);
       //把额外添加的profile加到环境对象里
      configureProfiles(environment, args);
    }

     

    默认添加的数量转化服务如下:

    converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

    converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
    converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

    converterRegistry.addConverter(new StringToCharacterConverter());
    converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

    converterRegistry.addConverter(new NumberToCharacterConverter());
    converterRegistry.addConverterFactory(new CharacterToNumberFactory());

    converterRegistry.addConverter(new StringToBooleanConverter());
    converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

    converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
    converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

    converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
    converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

    converterRegistry.addConverter(new StringToLocaleConverter());
    converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

    converterRegistry.addConverter(new StringToCharsetConverter());
    converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

    converterRegistry.addConverter(new StringToCurrencyConverter());
    converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

    converterRegistry.addConverter(new StringToPropertiesConverter());
    converterRegistry.addConverter(new PropertiesToStringConverter());

    converterRegistry.addConverter(new StringToUUIDConverter());
    converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());

    默认添加的集合Converter有

    converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
    converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

    converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
    converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
    converterRegistry.addConverter(new MapToMapConverter(conversionService));

    converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
    converterRegistry.addConverter(new StringToArrayConverter(conversionService));

    converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
    converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

    converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
    converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

    converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
    converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

    converterRegistry.addConverter(new StreamConverter(conversionService));

    其余默认加载的Converter

    converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
    converterRegistry.addConverter(new StringToTimeZoneConverter());
    converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
    converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

    converterRegistry.addConverter(new ObjectToObjectConverter());
    converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
    converterRegistry.addConverter(new FallbackObjectToStringConverter());
    converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));

    默认加载的Formatter

    formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());

    // Default handling of monetary values
    if (jsr354Present) {
      formatterRegistry.addFormatter(new CurrencyUnitFormatter());
      formatterRegistry.addFormatter(new MonetaryAmountFormatter());
      formatterRegistry.addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
    }

    // Default handling of date-time values

    // just handling JSR-310 specific date and time types
    new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);

    if (jodaTimePresent) {
      // handles Joda-specific types as well as Date, Calendar, Long
      new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
    }
    else {
      // regular DateFormat-based Date, Calendar, Long converters
      new DateFormatterRegistrar().registerFormatters(formatterRegistry);
    }

    加载的应用的Formatter

    registry.addFormatter(new CharArrayFormatter());
    registry.addFormatter(new InetAddressFormatter());
    registry.addFormatter(new IsoOffsetFormatter());

    加载应用的delimitedConverter

    registry.addConverter(new ArrayToDelimitedStringConverter(service));
    registry.addConverter(new CollectionToDelimitedStringConverter(service));
    registry.addConverter(new DelimitedStringToArrayConverter(service));
    registry.addConverter(new DelimitedStringToCollectionConverter(service));

    其余应用的Converter

    registry.addConverter(new StringToDurationConverter());
    registry.addConverter(new DurationToStringConverter());
    registry.addConverter(new NumberToDurationConverter());
    registry.addConverter(new DurationToNumberConverter());
    registry.addConverter(new StringToDataSizeConverter());
    registry.addConverter(new NumberToDataSizeConverter());
    registry.addConverter(new StringToFileConverter());
    registry.addConverterFactory(new LenientStringToEnumConverterFactory());
    registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());

     

    AbstractApplicationContext.refresh()方法

    @Override
    public void refresh() throws BeansException, IllegalStateException {
      synchronized (this.startupShutdownMonitor) {
         // Prepare this context for refreshing.
        //清空掉scanner的缓存,初始化当前对象的容器数据
         prepareRefresh();

         // Tell the subclass to refresh the internal bean factory.
          //获取beanFactory,这里是org.springframework.beans.factory.support.DefaultListableBeanFactory
         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      //初始化beanFactory
         // Prepare the bean factory for use in this context.
         prepareBeanFactory(beanFactory);

         try {
            // Allows post-processing of the bean factory in context subclasses.
             //
             //postProcessBeanFactory不知道是在干嘛,处理入口类上的扫描、其他注解
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.-》在上下文中把工厂处理器注册为bean
     //这里有一个很重要的事情就是把@Bean等bean标签做beanDefinition声明
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation. 注册拦截bean创建的处理器
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context. 为上下文初始化消息源
            initMessageSource();

            // Initialize event multicaster for this context. 为上下文初始化消息分发器
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses. 初始化特定容器子类中的特殊bean
            onRefresh();

            // Check for listener beans and register them. 检查监听器并注册他们
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons. 实例化所有尚未实例化的非懒加载单例
    //除了部分过程中要用的Bean自己单独进行实例化了,大部分Bean都是在这个环节进行实例化。单例实例都是存放在工厂方法singletonObjects中,这里采用的是ConcurrentHashMap作为存储容器,默认大小256
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event. 最后一步,发布对应的结束事件
            finishRefresh();
        }

         catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
               logger.warn("Exception encountered during context initialization - " +
                     "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources. 销毁已经创建的单例,避免资源浪费
            destroyBeans();

            // Reset 'active' flag. 重置 active标记
            cancelRefresh(ex);

            // Propagate exception to caller. 抛出异常
            throw ex;
        }

         finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
      }
    }

     

  • 相关阅读:
    linux 复 带进度条
    frp配置
    zookeeper_service 出错 java.lang.NoClassDefFoundError: org/I0Itec/zkclient/exception/ZkNoNodeException
    zookeeper_service 出错 ........... are only available on JDK 1.5 and higher
    推荐eclipse插件Properties Editor
    使用ab对nginx进行压力测试
    Linux搭建Snmp服务
    第一个python程序
    如何执行Python代码
    pycharm 的调试模式 MAC版
  • 原文地址:https://www.cnblogs.com/ybk2018af/p/12522352.html
Copyright © 2011-2022 走看看