SpringBoot启动类源码分析

二. SpringBoot启动类源码分析

  1. 一个基础的SpringBoot项目启动类,执行main方法中调用SpringApplication的run方法
@SpringBootApplication
public class App{
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);;
    }
}
  1. 在 SpringApplication 的run方法中会创建一个 SpringApplication 对象,在创建该对象时,会传递一个名为primarySources的Class类型数组,该数组正常情况下就是当前SpringBoot项目启动类.class,后续通过该class配合该类上修饰的注解使用
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }
  1. 创建 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));
        //判断当前项目时什么类型的,例如有DispatcherHandler,或Servlet如果引入了当前项目是web项目等等
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //初始化ApplicationContextInitializer初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//初始化ApplicationListener监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
  1. 初始化ApplicationListener监听器时会调用getSpringFactoriesInstances() 方法,配合@SpringBootApplication注解中的 XXXX注解读取项目中META-INF目录下的spring.factories配置文件,加载初始化配置文件中的类
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
  1. SpringApplication对象创建完毕后调用该对象的run方法
	public ConfigurableApplicationContext run(String... args) {
		//1创建一个计时器
        StopWatch stopWatch = new StopWatch();
        //计时器开始计时
        stopWatch.start();
        //初始化一些属性
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        //初始化一个AWT组件
        this.configureHeadlessProperty();
        //读取spring.factories配置文件中的所有的SpringApplicationRunListener
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //执行SpringApplicationRunListener回调starting()方法
        listeners.starting();

        Collection exceptionReporters;
        try {
        	//保存main方法执行时传递的args为ApplicationArguments 
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //构造SpringBoot项目的环境配置(读取yml文件,初始化项目中的配置)
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            //配置一些需要忽略的bean
            this.configureIgnoreBeanInfo(environment);
            //初始化 Banner 图并打印
            Banner printedBanner = this.printBanner(environment);
            //创建Spring的ApplicationContext容器对象
            context = this.createApplicationContext();
            //初始化异常打印信息
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //对当前启动的SpringBoot进行一些预先准备工作
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //初始化bean,到Spring容器中(与Spring中不同是,在SpringBoot中重写了方法中调用的onRefresh()方法,
            //判断当前使用的是外部Web容器还是内嵌Web容器,如果是内嵌容器的话就在内部启动一个Web容器)
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            //结束计时
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
			//回调SpringApplicationRunListener,started()
            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);
        }
    }

	private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        //读取配置文件中的META-INF目录下的spring.factories配置文件中的所有的SpringApplicationRunListener
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

	//构建SpringBoot环境
	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
		//获取已经构建好的环境(一般情况下是没有的)
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        //配置上下文环境
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        //执行所有SpringApplicationRunListener的回调方法environmentPrepared()
        //执行回调方法,会有一个专门的SpringApplicationRunListener读取yml文件(读取指定环境下的yml配置等等)
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }
		//对ConfigurationPropertySources中的配置进行合并
        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

	//创建ApplicationContext容器对象
	protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                //如果是servlet环境创建AnnotationConfigServletWebServerApplicationContext容器
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                //如果是REACTIVE创建nnotationConfigReactiveWebServerApplicationContext对象
                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);
    }

	//SpringBoot项目启动前的准备工作
	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        //1注入一些需要的bean组件
        context.setEnvironment(environment);
        //初始化准备工作
        this.postProcessApplicationContext(context);
        //2.执行ApplicationContextInitializer 的initialize(context)方法进行初始化
        this.applyInitializers(context);
        //3.执行SpringApplicationRunListener中的contextPrepared()回调方法
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
		//获取beanFactory 工厂
        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]));
        //将SpringApplicationRunListener监听器注入到容器中
        listeners.contextLoaded(context);
    }
  1. SpringBoot启动类要完成的工作总结
  • 通过是否引入了某个Class判断当前SpringBoot项目是web还是java,还是webFulx项目
  • 初始化 ApplicationContextInitializer 组件
  • 初始化META-INF目录下的spring.factories配置文件中的类
  • 初始化ApplicationListener监听器
  • 在SpringBoot中方法中重写了onRefresh()方法,判断当前使用的是外部Web容器还是内嵌Web容器,如果是内嵌容器的话就在内部启动一个Web容器)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值