SpringBoot – 06 – SpringBoot 2.x 启动流程源码解析

本文基于 SpringBoot 2.3.4.RELEASE 版本进行分析


1、启动类

@SpringBootApplication
public class SpringbootTestApplication {
   
   

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

该类主要为项目启动提供入口,通过 main() 方法即可启动整个 SpringBoot 项目

2、@SpringBootApplication 注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
   
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
   
   
    ...
}

该注解看似有那么多其他注解修饰,实际上我们只需要重点关注 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 这个三个注解即可

  • @SpringBootConfiguration

    • 继承自 @Configuration 注解,作用与其一致,用于标记当前类为配置类,相当于 XML 配置中的 <beans></beans>
  • @EnableAutoConfiguration

    • 开启自动配置,将所有符合自动配置条件的 Bean 对象加载进 IoC 容器中
  • @ComponentScan

    • 开启组件扫描,默认扫描当前类所在的包及其子包中的所有 Bean 对象,相当于 XML 配置中的 <context:component-scan></<context:component-scan>

3、SpringApplication 类

该类主要用于引导和启动一个 SpringBoot 应用程序

4、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);
}

run() 方法共有两个参数,如下所示

参数 作用
primarySource 需要加载的资源类
args 应用程序参数 (通常通过 main() 方法进行传递)

5、SpringApplication 对象

run()方法中,会先创建一个 SpringApplication 对象,然后再调用它的 run() 方法

SpringApplication 对象创建流程如下所示

public SpringApplication(Class<?>... primarySources) {
   
   
	this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   
   
    // 初始化资源加载器为 null
	this.resourceLoader = resourceLoader;
	// 断言判断要加载的资源是否不为 null,如果为 null,则会报错
	Assert.notNull(primarySources, "PrimarySources must not be null");
	// 初始化要加载的资源类集合并去重
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 推断当前 Web 引用类型
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// 设置应用上下文初始化器
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// 设置监听器
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 推断主入口应用类
	this.mainApplicationClass = deduceMainApplicationClass();
}
5.1、推断当前 Web 引用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();

static WebApplicationType deduceFromClasspath() {
   
   
	if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
			&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
   
   
		return WebApplicationType.REACTIVE;
	}
	for (String className : SERVLET_INDICATOR_CLASSES) {
   
   
		if (!ClassUtils.isPresent(className, null)) {
   
   
			return WebApplicationType.NONE;
		}
	}
	return WebApplicationType.SERVLET;
}

该方法主要用于根据类路径下是否有对应类型的项目来推断出不同的应用类型,共有以下三种应用类型

类型 含义
REACTIVE 响应式 Web 项目
NONE 非 Web 项目
SERVLET Servlet Web 项目
5.2 设置应用上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

先来看下 ApplicationContextInitializer 接口

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
   
   

	void initialize(C applicationContext);

}

该接口主要用于初始化给定的应用程序上下文

再来看下 getSpringFactoriesInstances() 方法

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
   
   
	return getSpringFactoriesInstances(type, new Class<?>[] {
   
   });
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
   
   
    // 获取当前应用上下文类加载器
	ClassLoader classLoader = getClassLoader();
	// 获取 ApplicationContextInitializer 实例名称集合并去重
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	// 根据指定类路径创建初始化实例列表
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	// 对初始化实例列表进行排序
	AnnotationAwareOrderComparator.sort(instances);
    // 返回初始化实例列表
	return instances;
}
  1. 获取 ApplicationContextInitializer 实例名称集合并去重

    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
         
         
    	String factoryTypeName = factoryType.getName();
    	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
    private static Map
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值