美文网首页
SpringBoot启动分析

SpringBoot启动分析

作者: 无玄 | 来源:发表于2022-04-09 07:01 被阅读0次
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

SpringApplication.run方法中,实例化一个SpringApplication对象,并调用该对象的run方法。

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        // 资源加载器
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 主要的资源类放入set,去重
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 推导web server的类型。通过classloader中是否加载了特定的class为依据,一般是servlet类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 设置BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener类型的Factory Instance
        // 遍历classloader中所有的META-INF/spring.factories中注册的信息,获取Factory Instance信息,并newInstance
        this.bootstrapRegistryInitializers = new ArrayList<>(
                getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 推导main的class。通过堆栈向上遍历,如果能找到main函数,则范围对应main函数的class,如果找不到,则返回null
        this.mainApplicationClass = deduceMainApplicationClass();
    }

在SpringApplication构造函数中,主要完成了这样两件事:

  • webApplicationType、mainApplicationClass的确认;
  • Configuration的自动装配。遍历所有META-INF/spring.factories,通过文件中的注册信息,获取到Factory Instance,及SpringApplication对象中BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener列表的赋值。
    /**
     * 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) {
        long startTime = System.nanoTime();
        // 创建应用引导上下文,并调用引导初始化器进行引导上下文的初始化
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
        // 获取SpringApplication启动的监听器 SpringApplicationRunListener 列表。从META-INF/spring.factories中获取监听器
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 构建 Environment
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            // 处理需要忽略的Bean
            configureIgnoreBeanInfo(environment);
            // 打印banner
            Banner printedBanner = printBanner(environment);
            // 创建应用上下文
            context = createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            // 准备应用上下文,应用上下文初始化、加载bean
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // 刷新应用上下文
            refreshContext(context);
            // 刷新应用上下文后的扩展方法
            afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
            }
            listeners.started(context, timeTakenToStartup);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }
        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

在run()中主要完成如下几项工作:

  • 创建应用引导上下文,并调用SpringApplication对象初始化时获取到的BootstrapRegistryInitializer进行BootstrapRegistryInitializer的初始化;
  • 通过SpringApplicationRunListener列表,并进行SpringApplication启动化时各阶段的监听,主要有如下方法:
    // 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
    void starting();
    // 当environment构建完成,ApplicationContext创建之前,该方法被调用
    void environmentPrepared(ConfigurableEnvironment environment);
    // 当ApplicationContext构建完成时,该方法被调用
    void contextPrepared(ConfigurableApplicationContext context);
    // 在ApplicationContext完成加载,但没有被刷新前,该方法被调用
    void contextLoaded(ConfigurableApplicationContext context);
    // 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
    void started(ConfigurableApplicationContext context);
    // 在run()方法执行完成前该方法被调用
    void running(ConfigurableApplicationContext context);
    // 当应用运行出错时该方法被调用
    void failed(ConfigurableApplicationContext context, Throwable exception);
  • 根据webApplicationType创建环境,并进行初始化;
  • ApplicationContext构建及加载;
    // ApplicationContext构建及加载
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        // 设置环境
        context.setEnvironment(environment);
        // 执行容器后置处理
        postProcessApplicationContext(context);
        // 调用ApplicationContextInitializer进行上下文的初始化
        applyInitializers(context);
        // 通知启动监听上下文已经准备好
        listeners.contextPrepared(context);
        // 关闭BootstrapContext
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        // 将启动相关bean注册进容器
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 将main函数中的args参数封装成单例Bean,注册进容器
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        //将 printedBanner 也封装成单例,注册进容器
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        // 获取启动参数中的sources,一般是Configuration配置类
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 加载sources中的bean
        load(context, sources.toArray(new Object[0]));
        //发布上下文已加载事件
        listeners.contextLoaded(context);
    }
  • ApplicationContext刷新;
@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.
        //这里是在子类中启动 refreshBeanFactory() 的地方
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context.
        //准备bean工厂,以便在此上下文中使用
        prepareBeanFactory(beanFactory);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            //设置 beanFactory 的后置处理
            postProcessBeanFactory(beanFactory);
            // Invoke factory processors registered as beans in the context.
            //调用 BeanFactory 的后处理器,这些处理器是在Bean 定义中向容器注册的
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            //注册Bean的后处理器,在Bean创建过程中调用
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            //对上下文中的消息源进行初始化
            initMessageSource();
            // Initialize event multicaster for this context.
            //初始化上下文中的事件机制
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            //初始化其他特殊的Bean
            onRefresh();
            // Check for listener beans and register them.
            //检查监听Bean并且将这些监听Bean向容器注册
            registerListeners();
            // Instantiate all remaining (non-lazy-init) singletons.
            //实例化所有的(non-lazy-init)单件
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            //发布容器事件,结束Refresh过程
            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();
        }
    }
}

在refreshContext方法中实现Ioc容器的初始化和Ioc的依赖注入。其中,在invokeBeanFactoryPostProcessors()方法中完成了IoC容器初始化过程的三个步骤。
  第一步:Resource定位
  在SpringBoot中,我们都知道他的包扫描是从主类所在的包开始扫描的,prepareContext()方法中,会先将主类解析成BeanDefinition,然后在refresh()方法的invokeBeanFactoryPostProcessors()方法中解析主类的BeanDefinition获取basePackage的路径。这样就完成了定位的过程。其次SpringBoot的各种starter是通过SPI扩展机制实现的自动装配,SpringBoot的自动装配同样也是在invokeBeanFactoryPostProcessors()方法中实现的。还有一种情况,在SpringBoot中有很多的@EnableXXX注解,细心点进去看的应该就知道其底层是@Import注解,在invokeBeanFactoryPostProcessors()方法中也实现了对该注解指定的配置类的定位加载。
  常规的在SpringBoot中有三种实现定位,第一个是主类所在包的,第二个是SPI扩展机制实现的自动装配(比如各种starter),第三种就是@Import注解指定的类。
  第二步:BeanDefinition的载入
  在第一步中说了三种Resource的定位情况,定位后紧接着就是BeanDefinition的分别载入。所谓的载入就是通过上面的定位得到的basePackage,SpringBoot会将该路径拼接成:classpath:org/springframework/boot/demo//.class这样的形式,然后一个叫做PathMatchingResourcePatternResolver的类会将该路径下所有的.class文件都加载进来,然后遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。大致过程就是这样的了。
TIPS:@Configuration,@Controller,@Service等注解底层都是@Component注解,只不过包装了一层罢了。
  第三步:注册BeanDefinition
  这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。
  invokeBeanFactoryPostProcessors方法中主要通过ConfigurationClassPostProcessor完成IoC容器初始化的过程。
  registerBeanPostProcessors方法中注册了AutowiredAnnotationBeanPostProcessor,registerBeanPostProcessors方法中对bean进行初始化时会调用AutowiredAnnotationBeanPostProcessor的接口实现@Autowired,完成Ioc的注入。

  • ApplicationRunner执行。

参考:
https://www.cnblogs.com/hello-shf/p/10992377.html
https://www.jianshu.com/p/b86a7c8b3442

相关文章

网友评论

      本文标题:SpringBoot启动分析

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