千家信息网

Springboot Code的启动源码是怎样的

发表于:2024-11-19 作者:千家信息网编辑
千家信息网最后更新 2024年11月19日,Springboot Code的启动源码是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。项目启动的流程:(一)new Spri
千家信息网最后更新 2024年11月19日Springboot Code的启动源码是怎样的

Springboot Code的启动源码是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

项目启动的流程:

(一)new SpringApplication

  1. 配置source和web环境;

  2. 创建初始化构造器和应用监听器;

  3. 配置应用的主方法所在类;

(二)run 第一二部分

1.获取并启动监听器

  • 初始化计时stopWatch、启动上下文bootstrapContext、设置系统参数headless;

  • 初始化监听器列表SpringApplicationRunListeners;

  • 发布springboot开始启动事件(从applicationListeners中过滤出4个能监听ApplicationStartingEvent事件的,并启动它们)

2.准备环境

  • 装配命令行参数applicationArguments(对象中装载4个propertySource);

  • 准备应用程序运行的环境ConfigurableEnvironment(从applicationListeners中过滤出6个能监听ApplicationEnvironmentPreparedEvent事件的,并启动它们。监听器中关联启动了一些后置处理器处理数据,最终目的是为应用环境做准备)

3.打印banner

方法:

     public ConfigurableApplicationContext run(String... args) {        //1、StopWatch简单的看成一个stop watch的机制,保存stop的记录信息。        //初始化一个计时器,并开始计时        StopWatch stopWatch = new StopWatch();        stopWatch.start();        //初始化启动上下文        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();        ConfigurableApplicationContext context = null;                //2、configureHeadlessProperty即配置headless模式,这种模式是一种系统缺少显示设备、键盘和鼠标外设的情况模式。        this.configureHeadlessProperty();        //3、SpringApplicationListeners为SpringApplicationRunListener接口实现集合(创建SpringApplicationRunListener初始化构造器)初始化监听器列表        //可以理解这个接口就是在spring启动整个过程都需要回调这些listener        //debug能发现,拿到了一个名为EventPublishingRunListener(RunListener构造方法中关联上了全部applicationListener),这个就是用来进行触发publishEvent的被观察者        SpringApplicationRunListeners listeners = this.getRunListeners(args);        //启动EventPublishingRunListener,从而过滤并启动相关Listener        listeners.starting(bootstrapContext, this.mainApplicationClass);        try {            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);            //4、ConfigurableEnvironment为配置环境对象,简单理解所有的配置信息汇总在这个对象中            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);            this.configureIgnoreBeanInfo(environment);            //5、Banner就是我们常在控制台输出的画面横幅,可以使用图片或者文本进行替换            Banner printedBanner = this.printBanner(environment);    =====>  //6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象            context = this.createApplicationContext();            context.setApplicationStartup(this.applicationStartup);            //7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);            //8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。            this.refreshContext(context);            //9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。            this.afterRefresh(context, applicationArguments);            //10、然后根据stopwatch打印出启动时间            stopWatch.stop();            if (this.logStartupInfo) {                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);            }            listeners.started(context);                         //11、这里调用ApplicationRunner和CommandLineRunner的实现类            this.callRunners(context, applicationArguments);        } catch (Throwable var10) {            this.handleRunFailure(context, var10, listeners);            throw new IllegalStateException(var10);        }        try {            listeners.running(context);            return context;        } catch (Throwable var9) {            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);            throw new IllegalStateException(var9);        }    }

下面开始对context的创建与准备

    context = this.createApplicationContext();    protected ConfigurableApplicationContext createApplicationContext() {        return this.applicationContextFactory.create(this.webApplicationType);    }
public interface ApplicationContextFactory {    ApplicationContextFactory DEFAULT = (webApplicationType) -> {        try {            switch(webApplicationType) {            case SERVLET:                return new AnnotationConfigServletWebServerApplicationContext();            case REACTIVE:                return new AnnotationConfigReactiveWebServerApplicationContext();            default:                return new AnnotationConfigApplicationContext();            }        } catch (Exception var2) {            throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);        }    };    ConfigurableApplicationContext create(WebApplicationType webApplicationType);    ....}

上述代码可以通过webApplicationTyep(即SERVLET)创建 AnnotationConfigServletWebServerApplicationContext 对象,对象创建过程中,初始化了父类的属性值,其中有三个比较关键的值,reader、scanner和父类中的beanFactory,下面这个对象的层级结构需要了解一下

/** * reader 和 scanner 都是在构造方法中进行了赋值**/public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {    private final AnnotatedBeanDefinitionReader reader;    private final ClassPathBeanDefinitionScanner scanner;    private final Set> annotatedClasses;    private String[] basePackages;    public AnnotationConfigServletWebServerApplicationContext() {        this.annotatedClasses = new LinkedHashSet();        this.reader = new AnnotatedBeanDefinitionReader(this);        this.scanner = new ClassPathBeanDefinitionScanner(this);    }    ...}

我们可以在 GenericApplicationContext 类的构造方法中看到其构造方法中,对beanFactory属性的赋值

    public GenericApplicationContext() {        this.customClassLoader = false;        this.refreshed = new AtomicBoolean();        this.beanFactory = new DefaultListableBeanFactory();    }

接下来对context进行配置与准备,看下prepareContext方法

//6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象            context = this.createApplicationContext();            context.setApplicationStartup(this.applicationStartup);    =====>  //7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);            //8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。            this.refreshContext(context);            //9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。            this.afterRefresh(context, applicationArguments);            //10、然后根据stopwatch打印出启动时间            stopWatch.stop();            if (this.logStartupInfo) {                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);            }            listeners.started(context);                         //11、这里调用ApplicationRunner和CommandLineRunner的实现类            this.callRunners(context, applicationArguments);
        /**         * Spring容器准备         */        private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,                                                                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,                                                                ApplicationArguments applicationArguments, Banner printedBanner) {                // 设置上下文环境                context.setEnvironment(environment);                //                postProcessApplicationContext(context);                // 执行所有ApplicationContextInitializer对象的initialize方法(这些对象是通过读取spring.factories加载)                applyInitializers(context);                // 发布上下文准备完成事件到所有监听器                listeners.contextPrepared(context);                bootstrapContext.close(context);                if (this.logStartupInfo) {                        logStartupInfo(context.getParent() == null);                        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());                }                // Load the sources                Set sources = getAllSources();                Assert.notEmpty(sources, "Sources must not be empty");                // 加载bean到上下文                load(context, sources.toArray(new Object[0]));                // 发送上下文加载完成事件                listeners.contextLoaded(context);        }    //为context设置beanFactoryPostProcess    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {        if (this.beanNameGenerator != null) {            context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator);        }        if (this.resourceLoader != null) {            if (context instanceof GenericApplicationContext) {                ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader);            }            if (context instanceof DefaultResourceLoader) {                ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader());            }        }        if (this.addConversionService) {            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());        }    }    //调用全部的构造器    protected void applyInitializers(ConfigurableApplicationContext context) {        Iterator var2 = this.getInitializers().iterator();        while(var2.hasNext()) {            ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();            Class requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");            initializer.initialize(context);        }    }    //加载class com.learning.demo.DemoApplication 启动类    protected void load(ApplicationContext context, Object[] sources) {        if (logger.isDebugEnabled()) {            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));        }        BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.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);        }        loader.load();    }

debug,此处load方法可以跟进多层到 AnnotatedBeanDefinitionReader 的 registerBean(启动类)

    public void registerBean(Class beanClass) {        this.doRegisterBean(beanClass, (String)null, (Class[])null, (Supplier)null, (BeanDefinitionCustomizer[])null);    }

这里同样有两个listeners.*的两个方法,和前面listeners.starting()是一样的,提供下最近整理的类图(了解Runlistener和listener类之间的关系)

//6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象            context = this.createApplicationContext();            context.setApplicationStartup(this.applicationStartup);            //7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);     =====> //8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。            this.refreshContext(context);            //9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。            this.afterRefresh(context, applicationArguments);            //10、然后根据stopwatch打印出启动时间            stopWatch.stop();            if (this.logStartupInfo) {                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);            }            listeners.started(context);                         //11、这里调用ApplicationRunner和CommandLineRunner的实现类            this.callRunners(context, applicationArguments);

refreshContext() 是整个Run方法的核心部分

   /**         * 刷新应用程序上下文         *         * @param context         */        private void refreshContext(ConfigurableApplicationContext context) {                // 注册一个关闭钩子,在jvm停止时会触发,然后退出时执行一定的退出逻辑                if (this.registerShutdownHook) {                        try {                                // 添加:Runtime.getRuntime().addShutdownHook()                                // 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook)                                context.registerShutdownHook();                        } catch (AccessControlException ex) {                                // Not allowed in some environments.                        }                }                // ApplicationContext真正开始初始化容器和创建bean的阶段                refresh((ApplicationContext) context);        }                protected void refresh(ApplicationContext applicationContext) {                Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);                refresh((ConfigurableApplicationContext) applicationContext);        }        protected void refresh(ConfigurableApplicationContext applicationContext) {                applicationContext.refresh();        }

调用应用上下文对象的refresh()方法,接下来我i门到ConfigurableApplicationContext类中去看下这个方法

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {        void refresh() throws BeansException, IllegalStateException;}

这是一个接口,且这个类是在spring框架中,非springboot,它的实现类共有三个

AbstractApplicationContext是一个抽象类,其余两个类都继承了它,我们来看看这个抽象类的代码:

      @Override        public void refresh() throws BeansException, IllegalStateException {                synchronized (this.startupShutdownMonitor) {                        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");                        // 第一步:准备更新上下时的预备工作                        prepareRefresh();                        // 第二步:获取上下文内部BeanFactory                        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();                        // 第三步:对BeanFactory做预备工作                        prepareBeanFactory(beanFactory);                        try {                                // 第四步:允许在上下文子类中对bean工厂进行post-processing                                postProcessBeanFactory(beanFactory);                                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");                                // 第五步:调用上下文中注册为bean的工厂 BeanFactoryPostProcessor                                invokeBeanFactoryPostProcessors(beanFactory);                                // 第六步:注册拦截bean创建的拦截器                                registerBeanPostProcessors(beanFactory);                                beanPostProcess.end();                                // 第七步:初始化MessageSource(国际化相关)                                initMessageSource();                                // 第八步:初始化容器事件广播器(用来发布事件)                                initApplicationEventMulticaster();                                // 第九步:初始化一些特殊的bean                                onRefresh();                                // 第十步:将所有监听器注册到前两步创建的事件广播器中                                registerListeners();                                // 第十一步:结束bean的初始化工作(主要将所有单例BeanDefinition实例化)                                finishBeanFactoryInitialization(beanFactory);                                // 第十二步:afterRefresh(上下文刷新完毕,发布相应事件)                                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();                                contextRefresh.end();                        }                }        }

这里有非常多的步骤,上下文对象主要的bean也是在这里进行处理的,具体的说明可以看注释, fresh方法就是SpringFrameWork的那部分(不再细化)

//6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象            context = this.createApplicationContext();            context.setApplicationStartup(this.applicationStartup);            //7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);            //8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。            this.refreshContext(context);     =====> //9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。            this.afterRefresh(context, applicationArguments);     =====> //10、然后根据stopwatch打印出启动时间            stopWatch.stop();            if (this.logStartupInfo) {                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);            }            listeners.started(context);                         //11、这里调用ApplicationRunner和CommandLineRunner的实现类            this.callRunners(context, applicationArguments);

afterRefresh() 是对方法 refresh() 的扩展,暂时空方法。

stopWatch.stop() 根据stopwatch打印出启动时间,至此项目已经启动完成。

* run方法主要做如下几件事情:

  1. 发出启动结束事件

  2. 执行实现ApplicationRunner、CommandLineRunner的run方法

  3. 发布应用程序已启动(ApplicationStartedEvent)事件

结合网上共享的两张图可以清晰回顾下整体流程:

关于Springboot Code的启动源码是怎样的问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。

0