美文网首页
springboot启动流程

springboot启动流程

作者: sunpy | 来源:发表于2022-08-29 22:20 被阅读0次

    前言


    最近打算扩展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方法,慢慢分析。

    相关文章

      网友评论

          本文标题:springboot启动流程

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