欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

简历写了解熟悉SpringBoot?请说一说 SpringBoot 的启动流程

程序员文章站 2022-04-17 15:52:30
...

springBoot启动流程分析:

springBoot启动流程主要分两步:

  1. new SpringApplication()应用
  2. 运行 SpringApplication()对象

进入run方法:

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

new SpringApplication()应用

进入new SpringApplication(primarySources)

    public SpringApplication(Class<?>... primarySources) {
        //传入一个null
        this((ResourceLoader)null, primarySources);
    }

    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;
        //初始化一个空的resourceLoader
        this.resourceLoader = resourceLoader;
        //断言primaySources 是否为空
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //初始化主要加载资源类,并去重
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        //判断web应用类型 NONE(非web项目),SERVLET(web项目),REACTIVE(响应式web项目);
        this.webApplicationType = WebApplicationType.deduceFromClasspath();

        //设置应用上下文 初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //设置监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

        //推断主入口类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

关键点:设置应用上下文初始化器、设置监听器

其两者做的事情是一样的,我们跟一下 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); 源码。

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        //载入所有的ApplicationContextInitializer的类全限定名
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //使用反射将所有的ApplicationContextInitializer实例化
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

关键点:载入所有的ApplicationContextInitializer的类全限定名

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        //取出所有 factoryTypeName(这里指ApplicationContextInitializer)所有类的全限定名
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    /**
    * 解析出所有 META-INF/spring.factories 文件的属性返回
    **/
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        //先从缓存取
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                //取出所有 META-INF/spring.factories 文件路径
                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 properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();
                    
                    //解析 Properties 
                    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);
            }
        }
    }

再看一下META-INF/spring.factories文件:

简历写了解熟悉SpringBoot?请说一说 SpringBoot 的启动流程

spring是在所有你加载的 jar 包中找到需要的 ApplicationContextInitializer 进行动态配置,只要你用到了特定的 maven 包,初始化时就会找到这个包下的 META-INF/spring.factories 中需要的类进行实例化bean,你就可以用了,不需要任何配置。

运行 SpringApplication()对象

    public ConfigurableApplicationContext run(String... args) {
        //开启启动计时器,项目启动完会打印执行时间出来
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();

        //获取SpringApplicationRunListener并启动监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //环境变量的加载  环境变量包括system environment、classpath environment和用户自己加的application.properties
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);

            //启动后console的打印出来的一堆配置信息   
            Banner printedBanner = this.printBanner(environment);

            //ApplicationContext实例化
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            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);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

创建applicationContext又分为几步:createApplicationContext、prepareContext、refreshContext。实例化applicationContext会根据在之前我们说的webEnvironment这个属性判断是使用webContext类AnnotationConfigEmbeddedWebApplicationContext还是普通context类AnnotationConfigApplicationContext(在这里我们使用的是webContext为例)然后通过反射进行实例化。applicationContext实例化完了会进入prepareContext流程,这个prepareContext方法会加载之前准备好的environment进入context中,然后如果有beanNameGenerator和resourceLoader那么提前创建bean加载进applicationContext,但是一般这两个都是空的,所以直接进入applyInitializers方法,将之前实例化的所有initializers进行初始化,所有的bean就是在这里进行bean的扫描和加载的因这次讲的是启动过程,所以不再细讲。最后是refreshContext,这个就和spring的bean加载过程一致了,bean的注入、beanFactory、postProcessBeanFactory等等,详情可以去看看spring bean的生命周期 。最后把创建好的applicationContext设置进入listener,prepareContext过程就结束了。(此段为引用)

总结:

1、创建SpringApplication实例,判定环境,是web环境还是普通环境。加载所有需要用到的Initializers和Listeners,这里使用约定大于配置的理念揭开了自动配置的面纱。
2、创建一个ApplicationContext,设置装配context。最终将装配好的context作为属性设置进SpringApplicationRunListeners。

参考博客:

spring boot启动流程图:https://www.pianshen.com/article/6036196723/

https://www.jb51.net/article/124286.htm

相关标签: springBoot