Bootstrap

深入浅出:SpringBoot启动流程源码分析(持续更新中......)最新日期:2024年10月29日

Hello,大家好,我是此林。

今天来深入底层讲一讲SpringBoot是如何启动的,也就是我们单击运行SpringBoot启动类,它底层发生了什么?

SpringBoot启动类很简单,只有一行代码。

我们点进run() 方法。

我们发现,它底层其实进行了两步操作。

第一步是new出一个SpringApplication对象,第二个是执行run() 方法。

我们先看看是如何new SpringApplication的。

继续点进SpringApplication的构造方法。

可以看到,这里初始化设置了非常多的属性。

我们主要最后几行关键代码。

this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();

通过DEBUG打断点我们发现,this.primarySources 就是当前启动类com.itheima.test.App。

也就是我们之前之前启动类里传入的参数App.class本身。

继续执行程序,这里判断了应用的类型。这里是servlet类型(传统Web应用),此外还有None类型(非Web应用)、Reactive类型。

再看这两行代码。

this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

1. 这里this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 底层是从 META-INF/spring.factories 文件里找到实现了 ApplicationContextInitializer 接口的所有实现类。它们用于在 ApplicationContext 初始化之前进行一些自定义的初始化操作。

以下是spring.factories 文件:

2. this.getSpringFactoriesInstances(ApplicationListener.class) 也一样,底层是从 META-INF/spring.factories 文件里找到键名为 

org.springframework.context.ApplicationListener

的值,也就是找到所有实现了 ApplicationListener 接口的类。它们用于监听 Spring 应用上下文中的事件。

以下是相关的spring.factories 文件:

为了更易于理解,我们举个例子,再深入看看 this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 源码。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
        // 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 对每一个实现类进行实例化
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 对实例数组进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

这里有三步:

1. 获取注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)

2. 对每一个实现类进行实例化(返回一个实例数组)

3. 对实例数组进行排序,表示执行顺序。排序规则如下:如果实现了 PriorityOrdered 接口,优先级最高。如果实现了Ordered接口或者基于注解@Order,数值越小优先级越高。

点进AnnotationAwareOrderComparator.sort() 方法。发现它调用了java.util.List的 sort(Comparator<? super E> c) 方法。这里传进去的INSTANCE是 AnnotationAwareOrderComparator 本身,说明 AnnotationAwareOrderComparator 实现了Comparator 接口,重写了 compare() 方法。

但是我们又发现 AnnotationAwareOrderComparator 类里似乎没有实现Comparator 接口实现自定义排序。

我们查看AnnotationAwareOrderComparator继承关系链发现,它继承了OrderComparator类;OrderComparator又实现了Comparator接口,所以AnnotationAwareOrderComparator 间接实现了Comparator 接口来自定义排序。

public int compare(@Nullable Object o1, @Nullable Object o2) {
        return this.doCompare(o1, o2, (OrderSourceProvider)null);
    }

    private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
        boolean p1 = o1 instanceof PriorityOrdered;
        boolean p2 = o2 instanceof PriorityOrdered;
        if (p1 && !p2) {
            return -1;
        } else if (p2 && !p1) {
            return 1;
        } else {
            int i1 = this.getOrder(o1, sourceProvider);
            int i2 = this.getOrder(o2, sourceProvider);
            return Integer.compare(i1, i2);
        }
    }

可以发现,主要功能是比较两个对象 o1 和 o2 的优先级顺序。
先检查 o1 和 o2 是否实现了 PriorityOrdered 接口。
如果 o1 实现了而 o2 没有实现,则 o1 的优先级更高,返回 -1。
如果 o2 实现了而 o1 没有实现,则 o2 的优先级更高,返回 1。
如果两者都实现了或都没有实现 PriorityOrdered 接口,则通过 getOrder 方法获取它们的顺序值,并比较这两个值。

我们再回到this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 源码。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
        // 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 对每一个实现类进行实例化
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 对实例数组进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

我们再来详细地看看这行代码的底层,它是如何获取ApplicationContextInitializer接口的所有实现类的?

// 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

点进loadFactoryNames() 方法。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

这里 loadSpringFactories(classLoaderToUse) 返回的是一个 Map<String, List<String>> ,getOrDefault() 则是取到键为 factoryTypeName 的 List,取不到则返回空集合。那么factoryTypeName到底是什么?我们发现它就是 ApplicationContextInitializer 接口。

由于刚才我们看过spring.factories 文件了,它就是一个键值对文件,它的值也就是一个类名列表,所以我们推测 Map<String, List<String>> 就是整个spring.factories。

继续点进 loadSpringFactories() 方法。果然如此,详细请看注释。

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            Map<String, List<String>> result = new HashMap();

            try {
                // 加载出了所有META_INF/spring.factories文件
                Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
                // 遍历全部的META_INF/spring.factories文件
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    // 每一个META_INF/spring.factories文件相当于是一个properties文件
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();
                    // 转成Map格式,key是接口名,value是实现类的列表
                    while(var6.hasNext()) {
                        Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

我们要扫描所有的所有META_INF/spring.factories文件,因为它可能不止一个。

我们再来总结一下,以上流程就是创建初始化SpringApplication对象,并设置相关属性。关键的还是这几行代码。

​
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();

​

至此,我们SpringApplication对象的创建就结束了。

明天我们再来详细讲一讲SpringApplication对象的run() 方法。

本文持续更新中....... 

关注我吧!老朋友此林,带你看不一样的世界!有疑惑的小伙伴可以私信我或者评论区留言,我看到后会及时回复!

往期文章:

SpringBoot自动配置原理:底层源码分析-CSDN博客

进阶版:深入浅出 Spring AOP底层原理分析(附自定义注解案例)(二)更新已完结-CSDN博客

;