本文基于 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>
- 开启组件扫描,默认扫描当前类所在的包及其子包中的所有 Bean 对象,相当于 XML 配置中的
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;
}
-
获取 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