SpringBoot整体启动流程分析

逐步分析SpringBoot的整体启动流程

入口分析

  • 标注@SpringBootApplication的启动类为入口

    @SpringBootApplication
    public class SpringMain {
        public static void main(String[] args) {
            SpringApplication.run(SpringMain.class, args);
        }
    
    }
    
  • SpringApplication.run(SpringMain.class, args)为入口开始追踪源码 底层源码

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

    本质上new SpringApplication 传入启动类的class 调用run方法

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }
    
  • 分别来分析new SpringApplication 做了什么 和 run方法做了什么

new SpringApplication构造方法

构造方法源码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    // 判断主启动类是否为null
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 设置主启动类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 判断是否WebFlux 或者 Servlet 的Web环境
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 读取spring.factories中的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 读取spring.factories中的ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 设置主启动类
    this.mainApplicationClass = deduceMainApplicationClass();
}
  • 检查启动类 是否为空 如果为空 抛出异常

  • WebApplicationType.deduceFromClasspath()方法检查是否是web环境, 本质上通过ClassUtils的方法来判断是否加载了WebFlux的包或者WebServlet的包

    static WebApplicationType deduceFromClasspath() {
        // 检查是否是响应式web环境 本质上判断 org.springframework.web.reactive.DispatcherHandler是否加载
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        // 检查是否是不同的web环境本质上判断是否加载 "javax.servlet.Servlet",
    	//"org.springframework.web.context.ConfigurableWebApplicationContext"
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }
    
  • setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 获取所有的Initializer 初始化器 放到容器中

    本质是是读取spring.factory文件获取所有的初始化器 核心方法是getSpringFactoriesInstances中的SpringFactoriesLoader.loadFactoryNames 获取到所有的ApplicationContextInitializer

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }
    
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // 核心SpringFactoriesLoader去加载
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 根据注解的@Order排序
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    

    SpringFactoriesLoader.loadFactoryNames方法详细

    	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    		String factoryTypeName = factoryType.getName();
    		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    	}
    
    	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    		MultiValueMap<String, String> result = cache.get(classLoader);
    		if (result != null) {
    			return result;
    		}
    
    		try {
    			Enumeration<URL> urls = (classLoader != null ?
    // FACTORIES_RESOURCE_LOCATION = META-INF/spring.factories
    					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    			result = new LinkedMultiValueMap<>();
                // 遍历中找到的资源
    			while (urls.hasMoreElements()) {
    				URL url = urls.nextElement();
    				UrlResource resource = new UrlResource(url);
                    // 使用PropertiesLoaderUtils工具来读取文件的properties文件
    				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    				for (Map.Entry<?, ?> entry : properties.entrySet()) {
    					String factoryTypeName = ((String) entry.getKey()).trim();
    					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    						result.add(factoryTypeName, factoryImplementationName.trim());
    					}
    				}
    			}
    			cache.put(classLoader, result);
    			return result;
    		}
    		catch (IOException ex) {
    			throw new IllegalArgumentException("Unable to load factories from location [" +
    					FACTORIES_RESOURCE_LOCATION + "]", ex);
    		}
    	}
    
  • setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));跟上一步找到Initializer的方法一样 也是从META-INF/spring.factories找ApplicationListener的子类

  • this.mainApplicationClass = deduceMainApplicationClass(); 找到启动main方法的类设置给mainApplicationClass属性

以上则是 new SpringApplication构造器的全部流程 总结

  • 判断主启动类是否存在
  • 判断当前应用是否是web应用 WebApplicationType.deduceFromClasspath()
  • 如果是SpringBoot2.4.0之后的版本还有获取所有的Bootstrapper,也是从spring.factories中获取 接口中只有一个方法void intitialize(BootstrapRegistry registry);
  • 设置所有的ApplicationContextInitializer 初始化器setInitializers
  • 设置所有的ApplicationListener监听器 setListeners

进入SpringApplication.run 方法进一步分析 Spring的启动流程

public ConfigurableApplicationContext run(String... args) {
    // new StopWatch
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置应用为无头headless模式  java.awt.headless属性
    configureHeadlessProperty();
    // 从spring.factories获取所有的SpringApplicationRunListeners 
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 在创建上下文之前 调用所有SpringApplicationRunListeners的starting方法
    listeners.starting();
    try {
        // 将传入参数封装成ApplicationArguments
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 准备环境 返回ConfigurableEnvironment
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 设置忽略的bean
        configureIgnoreBeanInfo(environment);
        // 打印Banner
        Banner printedBanner = printBanner(environment);
       	// 根据环境创建不同类型的上下文 Servlet环境就是AnnotationConfigServletWebServerApplica
        context = createApplicationContext();
        // 从spring.factories获取所有的SpringBootExceptionReporter 异常报告器
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        // 初始化上下文之前的准备工作 如添加一些特殊的单实例bean 调用监听器的contextLoaded方法
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 最重要的IOC容器创建核心方法
        refreshContext(context);
        // 空实现供子类覆盖
        afterRefresh(context, applicationArguments);
        // stopWatch stop 记录容器创建时间
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 容器创建完成 调用监听器的started
        listeners.started(context);
        // 调用所有ApplicationRunner 和 CommandLineRunner 的run方法
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 如果出现异常调用监听器的failed方法 和 exceptionReporters记录错误
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 回调所有监听器的running方法
        listeners.running(context);
    }
    catch (Throwable ex) {
        // 如果出现异常调用监听器的failed方法 和 exceptionReporters记录错误
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
  • 创建stopWatch 用于监听应用启动的时间

  • 设置系统变量为headless模式 configureHeadlessProperty java.awt.headless属性

  • 获取所有的SpringApplicationRunListeners getRunListeners方法 从META-INF中获取找所有的SpringApplicationRunListener类 默认只有一个EventPublishingRunListener

    image-20210219150328755

  • 调用所有SpringApplicationRunListener的 starting方法 在Spring上下文创建之前

  • 保存命令行参数到ApplicationArguments

  • 准备环境 prepareEnvironment方法

    	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    			ApplicationArguments applicationArguments) {
    		// 创建一个环境
    		ConfigurableEnvironment environment = getOrCreateEnvironment();
    		configureEnvironment(environment, applicationArguments.getSourceArgs());
    		ConfigurationPropertySources.attach(environment);
            // 调用监听器的环境准备完毕方法
    		listeners.environmentPrepared(environment);
            // 绑定环境变量
    		bindToSpringApplication(environment);
    		if (!this.isCustomEnvironment) {
    			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
    					deduceEnvironmentClass());
    		}
    		ConfigurationPropertySources.attach(environment);
    		return environment;
    	}
    
    • 根据环境创建上下文环境 如Servlet环境返回StandardServletEnvironment
    • 设置环境变量信息和profile
    • 所有监听器调用environmentPrepared方法 发布环境准备完毕事件
    • 绑定环境变量到Spring的环境中
  • 设置IngnoreBean信息 忽略bean信息

  • 打印Banner 默认找banner.txt或者通过spring.banner.location自定义banner位置或者图片banner spring.banner.image.location支持gif jpg png 具体参考SpringApplicationBannerPrinter类

  • 根据具体不同的环境创建Spring上下文环境,如果是Servlet环境默认创建AnnotationConfigServletWebServerApplicationContext上下文 createApplicationContext

  • 从spring.factories找到所有的SpringBootExceptionReporter

  • 准备环境prepareContext

    	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
            // 上下文环境设置环境信息
    		context.setEnvironment(environment);
    		postProcessApplicationContext(context);
            // 执行所有初始化器的 initialize方法
    		applyInitializers(context);
            // 环境准备完成 调用所有listeners中的contextPrepared
    		listeners.contextPrepared(context);
    		if (this.logStartupInfo) {
    			logStartupInfo(context.getParent() == null);
    			logStartupProfileInfo(context);
    		}
    		// 创建一些特殊的单例bean
    		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<Object> sources = getAllSources();
    		Assert.notEmpty(sources, "Sources must not be empty");
    		load(context, sources.toArray(new Object[0]));
            // 在上下文装载完成 还没初始化 的时候回调listenercontextLoaded
    		listeners.contextLoaded(context);
    	}
    
    • 上下文环境设置
    • applyInitializers 执行所有的初始化器的initialize方法 对IOC容器进行初始化扩展功能
    • 调用所有监听器的contextPrepared方法 环境准备之前的回调
    • 打印启动信息 和 profile信息
    • 拿到beanFactory添加一些特殊的单例的bean 如springApplicationArguments springBootBanner
    • 监听器的contextLoaded 方法 回调环境已经加载完毕
  • 核心方法refreshContext中的refresh 单独写一篇文章详细分析 IOC容器创建的核心

  • afterRefresh方法调用 空实现供子类重写

  • stopWatch.stop 记录启动时间

  • 回调所有监听器的started方法 表示IOC容器启动完成.

  • callRunners方法 调用 容器中所有 ApplicationRunner和 CommandLineRunner 调用他们的run方法

  • 调用所有监听器的running方法 listeners.running

  • 如果程序在任何时候出现异常调用 handleRunFailure 设置错误退出码调用所有listener的failed方法调用SpringBootExceptionReporter 记录错误信息

    image-20210219160541540

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值