美文网首页
Spring Boot源码旅程

Spring Boot源码旅程

作者: 雁阵惊寒_zhn | 来源:发表于2022-01-11 10:34 被阅读0次

    基于Spring Boot 1.5.7.RELEASE版本,梳理Spring Boot Web程序启动流程。相较于Spring,Spring Boot启动流程中主要源码逻辑在本文中直接阐述。与Spring通用或者特色的复杂逻辑在其他文章阐述。

    Step0. 我们的Spring Boot源码旅程从下面Spring Boot Web程序入口开始。

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

    Step1. 创建并且运行一个SpringApplication,SpringApplication()构造函数调用内部函数initialize()初始化其内部数据。

    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
      return new SpringApplication(sources).run(args);
    }
    public SpringApplication(Object... sources) {
        initialize(sources);
    }
    

    setInitializers()方法,设置的Initializers对象在ConfigurableApplicationContext上下文对象被refresh之前(run方法中的prepareContext中调用)对上下文进行一些初始化操作。
    setListeners()方法设置监听器,当上下文对象触发了某个监听器监听的确切事件时,添加到其中的监听器就会自动被触发执行监听器的onApplicationEvent()函数逻辑。

    private void initialize(Object[] sources) {
      if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
      }
      this.webEnvironment = deduceWebEnvironment();
      setInitializers((Collection) getSpringFactoriesInstances(
          ApplicationContextInitializer.class));
      setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
      this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    Step2. 运行执行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 = new StopWatch();
      stopWatch.start();
      ConfigurableApplicationContext context = null;
      FailureAnalyzers analyzers = null;
      configureHeadlessProperty();
      SpringApplicationRunListeners listeners = getRunListeners(args);
      listeners.starting();
      try {
        //Step3
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //Step4
        Banner printedBanner = printBanner(environment);
        //Step5
        context = createApplicationContext();
        analyzers = new FailureAnalyzers(context);
        //Step6
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //Step7
        refreshContext(context);
        //Step8
        afterRefresh(context, applicationArguments);
        //Step9
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
          new StartupInfoLogger(this.mainApplicationClass)
              .logStarted(getApplicationLog(), stopWatch);
        }
        return context;
      }
      catch (Throwable ex) {
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
      }
    }
    

    Step3. main()函数传入用户参数,经过SpringApplication的run()方法传递,最后通过类ApplicationArguments进行处理。我们可以在程序中通过注入这个bean获取参数。
    类ConfigurableEnvironment进行环境配置。

    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    

    Step4. printBanner()方法打印Spring Boot启动的横幅,会扫描参数"banner.image.location"的value;或者扫描类路径下"gif", "jpg", "png"三种图片格式,名为banner的图片;或者读取"banner.location"的value(默认值为"banner.txt",在resouces目录下定义同名的"banner.txt"文件可以设置自定义的启动横幅)。

    Banner printedBanner = printBanner(environment);
    

    默认的Banner。


    图片选自我的公众号

    Step5. 创建上下文对象,也就是在Spring中常提到的容器。Web环境中利用反射机制默认创建的是AnnotationConfigEmbeddedWebApplicationContext对象。

    context = createApplicationContext();
    
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
          try {
            contextClass = Class.forName(this.webEnvironment
                ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
          }
          catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                "Unable create a default ApplicationContext, "
                    + "please specify an ApplicationContextClass",
                ex);
          }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }
    

    创建上下文对象的同时也会创建默认的BeanFactory对象,具体创建过程在父类GenericApplicationContext的无参数构造函数中。

    public GenericApplicationContext() {
        this.beanFactory = new DefaultListableBeanFactory();
    }
    

    Step6. 在refresh上下文之前,先进行准备工作。包括向上下文对象context中添加环境相关的配置、应用Step1中提到的Initializers对象、设置指定的单例bean(设置Step4提到的springBootBanner和设置Step3提到springApplicationArguments)等。

    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    
    private void prepareContext(ConfigurableApplicationContext context,
          ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
          ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
          logStartupInfo(context.getParent() == null);
          logStartupProfileInfo(context);
        }
    
        // Add boot specific singleton beans
        context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);
        if (printedBanner != null) {
          context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }
    
        // Load the sources
        Set<Object> sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);
    }
    

    Step7. refreshContext()函数是Spring注入Bean的关键步骤。

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

    进入refresh()。

    /**
     * Refresh the underlying {@link ApplicationContext}.
     * @param applicationContext the application context to refresh
     */
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext) applicationContext).refresh();
    }
    

    再进入applicationContext.refresh()。这里的refresh()函数就是Spring Boot启动的核心,虽然具体实现类略有不同,但是与Spring大体是相同的逻辑。具体关注我的公众号《魔法师和ta的南瓜小屋》或简书其他文章会详细介绍。

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
          // Prepare this context for refreshing.
          prepareRefresh();
    
          // Tell the subclass to refresh the internal bean factory.
          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
          // Prepare the bean factory for use in this context.
          prepareBeanFactory(beanFactory);
    
          try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
    
            // Invoke factory processors registered as beans in the context.
            //对注解的Bean转换为BeanDefinition结构
            invokeBeanFactoryPostProcessors(beanFactory);
    
            // Register bean processors that intercept bean creation.
            //注册bean post processors
            //AOP的实现就是利用注册的AnnotationAwareAspectJAutoProxyCreator
            registerBeanPostProcessors(beanFactory);
    
            // Initialize message source for this context.
            initMessageSource();
    
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
    
            // Initialize other special beans in specific context subclasses.
            onRefresh();
    
            // Check for listener beans and register them.
            registerListeners();
    
            // Instantiate all remaining (non-lazy-init) singletons.
            //单例Bean的创建
            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.
            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();
          }
        }
    }
    

    Step8. Spring Boot注入所有Bean后,进行收尾工作,如果程序中自定义并实现了ApplicationRunner和CommandLineRunner接口的Bean对象,在这里会被调用执行。

    afterRefresh(context, applicationArguments);
    

    执行callRunner()函数调用ApplicationRunner和CommandLineRunner实例对象的run()方法执行。

    protected void afterRefresh(ConfigurableApplicationContext context,
          ApplicationArguments args) {
        callRunners(context, args);
      }
    
    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<Object>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<Object>(runners)) {
          if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
          }
          if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
          }
        }
    }
    

    Step9. 调用监听器回调方法。启动成功发送事件ApplicationReadyEvent,否则发送失败事件ApplicationFailedEvent。

    listeners.finished(context, null);
    

    我们可以自定义监听器接收事件,并且完成特定的功能。例如下面代码,当启动成功发送事件ApplicationReadyEvent时,onApplicationEvent()函数打印日志。

    
    @Component
    public class SloganReadyEventListener implements ApplicationListener<ApplicationReadyEvent>, Ordered {
        @Override
        public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
            System.out.println("每一次的启动都是对梦想的追逐。你好,追逐梦想路上的人。");
        }
    
        @Override
        public int getOrder() {
            return Ordered.LOWEST_PRECEDENCE;
        }
    }
    

    Step10. 自此Spring Boot完成启动。

    相关文章

      网友评论

          本文标题:Spring Boot源码旅程

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