SpringBoot启动配置原理

15 篇文章 0 订阅
6 篇文章 0 订阅

SpringBoot启动配置原理

    SpringBoot最大的优势就是自动化配置,只需将需要的配置按照一定的规则,加上@Configuration @ConfigurationProperties @EnableAutoConfigurations 注解,便可以自动将需要的数据加入到IOC容器中。使用非常方便快捷,同时降低代码量,无需XML文件另外进行配置。那么SpringBoot底层到底做了哪些工作,它的启动原理,运行流程,自动配置到底是怎么样,下面简要分析一下。

1 SpringBoot启动配置原理

    SpringBoot启动配置主要关注点是几个重要的事件回调机制 ApplicationContextInitializer ,SpringApplicationRunListener,ApplicationRunner,CommandLineRunner,下面我们从主配置类@SpringBootApplication的main方法进行启动流程分析。

  • 启动流程
      1、通过main方法创建SpringApplication对象,再执行run()方法
        public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
               return (new SpringApplication(primarySources)).run(args);
    }  

那么创建SpringApplication是怎么样的呢?在创建过程中都干了什么?分析如下:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //这一步是将主配置类保存起来
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources))*;
        //这一步是判断当前环境是不是web环境,里面有Servlet,所以是web环境
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //这一步非常关键,是获取所有WEB-INF/spring.factories下面的初始化器,如下是加载的方法
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //这一步是获取所有的WEB-INF/spring.factories下面的Listener监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //最后一步,获取启动类,也就是SpringBoot的main方法
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

       MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
       if (result != null) {
           return result;
       } else {
           try {
               Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : 
               //这里就是加载所有的Initializer
               ClassLoader.getSystemResources("META-INF/spring.factories");
               LinkedMultiValueMap result = new LinkedMultiValueMap();

               while(urls.hasMoreElements()) {
                   URL url = (URL)urls.nextElement();
                   UrlResource resource = new UrlResource(url);
                   Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                   Iterator var6 = properties.entrySet().iterator();

                   while(var6.hasNext()) {
                       Entry<?, ?> entry = (Entry)var6.next();
                       String factoryTypeName = ((String)entry.getKey()).trim();
                       String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                       int var10 = var9.length;

                       for(int var11 = 0; var11 < var10; ++var11) {
                           String factoryImplementationName = var9[var11];
                           result.add(factoryTypeName, factoryImplementationName.trim());
                       }
                   }
               }

               cache.put(classLoader, result);
               return result;
           } catch (IOException var13) {
               throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
           }
       }
   }

然后SpringApplication进行反射创建对象,集合如下图:
初始化器集合
获取监听器代码示例:

        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    } 

下面是获取到的监听器全类名:在这里插入图片描述
可以看到,上述SpringBoot在创建SpringApplication的对象过程还是比较复杂,但是最重要还是实例化自己的一些属性信息。下面看一看到底执行run()方法的启动流程是怎么样的,

  • SpringApplication的run()方法:
  public ConfigurableApplicationContext run(String... args) {
      //把停止的监听启动
      StopWatch stopWatch = new StopWatch();
      stopWatch.start();
      //空的IOC容器
      ConfigurableApplicationContext context = null;
      Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
      this.configureHeadlessProperty();
      //重点来了,获取SpringApplicationRunListeners ,并将它启动(EventPubshingRunListener)。这里的获取方式和上一步一样,都是在类路径下META-INF/spring.factories下面获取。
      SpringApplicationRunListeners listeners = this.getRunListeners(args);
      //这里的starting()是启动所有之前获取到的监听器,starting()代码如下:
      listeners.starting();

      Collection exceptionReporters;
      try {
      //这里表明是将命令行参数用ApplicationArguments 进行封装了一下
          ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          //准备环境
          ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
          //创建环境完成后回调SpringApplicationRunListener
          this.configureIgnoreBeanInfo(environment);
          //这一步就是打印Banner图标
          Banner printedBanner = this.printBanner(environment);
          //非常关键的一步,创建IOC容器,创建过程如下面代码,主要是分析是不是web环境,SpringBoot创建的是`AnnotationConfigServletWebServerApplication`
          context = this.createApplicationContext();
          //出现异常的回调方式
          exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
          //这里又是非常重要的一步,IOC容器创建完成后,准备上下文
          this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          //刷新环境,刷新容器,Spring里面最重要的过程,扫描所有的初始化类,IOC初始化过程,前置通知、后置通知、单实例Bean(这个是IOC容器的重中之重),同时,如果是web应用,这里面还会初始化嵌入式的Tomcat
          this.refreshContext(context);
          //从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。同时调用Spring的finish方法
          this.afterRefresh(context, applicationArguments);
          //保存应用状态,启动完成
          stopWatch.stop();
          if (this.logStartupInfo) {
              (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
          }

          listeners.started(context);
          this.callRunners(context, applicationArguments);
      } catch (Throwable var10) {
          this.handleRunFailure(context, var10, exceptionReporters, listeners);
          throw new IllegalStateException(var10);
      }

      try {
          listeners.running(context);
          //启动完成后,返回IOC容器
          return context;
      } catch (Throwable var9) {
          this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
          throw new IllegalStateException(var9);
      }
  }

starting()代码块:

     Iterator var1 = this.listeners.iterator();

     while(var1.hasNext()) {
         SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
         listener.starting();
     }

 }

下面可以看到listenersListeners
创建IOC容器

protected ConfigurableApplicationContext createApplicationContext() {
     Class<?> contextClass = this.applicationContextClass;
     if (contextClass == null) {
         try {
             switch(this.webApplicationType) {
             case SERVLET:
                 contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                 break;
             case REACTIVE:
                 contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                 break;
             default:
                 contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
             }
         } catch (ClassNotFoundException var3) {
             throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
         }
     }

     return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
 }

IOC容器创建完成后准备上下文

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//准备上下文第一步就是把environment保存到IOC容器中
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        //这一步非常重要,就是把我们第一次创建SpringApplication的时候,将保存的Intializers实例进行回调
        this.applyInitializers(context);
        //回调之前保存好的SpringApplicationRunListener的contextPrepared()方法
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        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 = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        //prepareContext()方法完全完成以后,回调所有的contextLoaded()方法
        listeners.contextLoaded(context);
    }

总结
SpringBoot最重要的就是几个回调机制:
1. ApplicationContextInitialer, SpringApplicationRunListener这两个配置放在配置当中。
2. ApplicationRunner,CommandRunner (放入到IOC容器中)
3. 监听器的启动,初始化器的启动,IOC容器的刷新。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值