springboot启动流程

2022-08-29  本文已影响0人  sunpy

前言


最近打算扩展mybatis-plus,但是都是整合在springboot中,所以打算搞清楚两件事,一件事就是springboot启动都加载了哪些bean,yml文件怎么处理的,第二件事,就是请求数据库,mybatisplus如何处理的。

springboot源码版本

2.6.3

springboot程序入口


执行main方法,先不关心那个@SpringBootApplication注解:

@SpringBootApplication
public class SourceCodeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SourceCodeApplication.class, args);
    }
}

调用run方法:

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

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

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));
    // 判断使用的容器类型
    /**
     * 有这个org.springframework.web.reactive.DispatcherHandler类,
     * 没有这个org.springframework.web.servlet.DispatcherServlet类,
     * 你那么采用reactive响应式编程,否则默认采用sevlet
     */
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
            getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 加载classpath下META-INF/spring.factories中配置的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 加载classpath下META-INF/spring.factories中配置的ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 初始化入口main类
    this.mainApplicationClass = deduceMainApplicationClass();
}

getSpringFactoriesInstances(ApplicationContextInitializer.class)加载classpath下META-INF/spring.factories:

读取META-INF/spring.factories文件,加载接口名和类名,生成map映射关系,返回map。

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    // 获取类加载器
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    result = new HashMap<>();
    try {
        // FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                        StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    // 存入接口的类型名称,对应value值为实现类的名称
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                            .add(factoryImplementationName.trim());
                }
            }
        }

        // 消除重复元素,生成不变的map
        result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
        // 将加载的类都缓存到cache
        // Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
        cache.put(classLoader, result);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    return result;
}

反射实例化类:

getSpringFactoriesInstances(ApplicationListener.class)加载classpath下META-INF/spring.factories:

this.mainApplicationClass = deduceMainApplicationClass();初始化入口main类

SpringApplication对象的run方法


public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    // 设置系统变量java.awt.headless
    configureHeadlessProperty();
    // 加载SpringApplicationRunListener监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 创建配置environment
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        // spring.beaninfo.ignore字段,忽略配置bean信息,
        configureIgnoreBeanInfo(environment);
        // 打印banner,SpringBoot启动时,控制台输出的一个歪歪扭扭的很不清楚的Spring几个大字母,也可以自定义
        Banner printedBanner = printBanner(environment);
        // 根据this.webApplicationType配置是加载reactive响应式编程,还是默认的servlet,创建上下文context
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        // 将前面配置的environment设置到context上下文
        // springApplicationArguments、springBootBanner单例模式实例化
        // 初始化循环依赖配置为false、BeanDefinition重写覆盖为false
        // 初始化懒加载后置处理器
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 加载bean,里面调用refresh方法与之前的spring套路一样了
        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;
}

refreshContext方法调用refresh方法,实现初始化容器


private void refreshContext(ConfigurableApplicationContext context) {
    if (this.registerShutdownHook) {
        shutdownHook.registerApplicationContext(context);
    }
    refresh(context);
}

老版spring加载xml文件,AbstractApplicationContext类之refresh方法加载bean:
参考:https://www.jianshu.com/p/56f863a44d96

springboot加载bean,实现refresh方法,慢慢分析。

上一篇下一篇

猜你喜欢

热点阅读