这是一个SpringBoot项目的启动类
SpringApplication的run方法是将当前类和参数传递进去进行初始化操作
run方法会返回一个SpringApplication实例,进行启动初始化。
接着往下看,new SpringApplication(primarySources) 这段代码,我们点击SpringApplication,可以看到如下图
给核心变量赋值
在这么多变量中,我们找到核心的几个
//主应用程序类
private Class<?> mainApplicationClass;
//主要bean来源集合
private Set<Class<?>> primarySources;
//应用程序初始化器集合
private List<ApplicationContextInitializer<?>> initializers;
//应用程序监听器集合
private List<ApplicationListener<?>> listeners;
//应用程序类型
private WebApplicationType webApplicationType;
//创建一个新的SpringApplication实例,并从bean来源集合中加载信息
public SpringApplication(Class<?>... primarySources) {
//空的资源加载器
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
//判断来源bean集合是否为空
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//设置Web应用程序类型,可能是SERVLET程序,也可能是REACTIVE响应式程序,还有可能是NONE即非web应用程序
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
//设置ApplicationContextInitializer,应用程序初始化器。从META-INF/spring.factories文件中获取 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置ApplicationListener,应用程序监听器。从META-INF/spring.factories文件中获取
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
判断应用程序类型
deduceFromClasspath()该方法根据是否存在指定路径的类来推断应用程序类型。有NONE、REACTIVE、SERVLET三种,一般都是SERVLET类型。
源码如下
static WebApplicationType deduceFromClasspath() {
//如果存在org.springframework.web.reactive.DispatcherHandler类型,并且不存在org.springframework.web.servlet.DispatcherServlet类型,并且不存在org.glassfish.jersey.servlet.ServletContainer类型,那么设置为REACTIVE,即响应式web应用程序
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
//如果不存在javax.servlet.Servlet类型,或者不存在org.springframework.web.context.ConfigurableWebApplicationContext类型,那么设置为NONE,即非web应用程序
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
//否则设置为SERVLET,即给予servlet的web应用程序,一般都是SERVLET
return SERVLET;
}
}
设置初始化器
设置ApplicationContextInitializer初始化器,初始化的时候会使用到,在Spring上下文被刷新之前进行初始化的操作,例如注入Property Sources属性源或者是激活Profiles环境,这里会借助SpringFactoriesLoader工具类获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例,也就是ApplicationContextInitializer类型的实例。
spring.factories 是Spirng boot提供的一种扩展机制,实际上spring.factories就是仿照Java中的SPI扩展机制来实现的Spring Boot自己的SPI机制,它是实现Spribf Boot的自动配置的基础。
spring.factories该文件必须是 Properties 格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。
设置初始化器源码
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList(initializers);
}
从META-INF/spring.factories中加载实例源码
//借助SpringFactoriesLoader获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例spring.factories 文件必须是 Properties 格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据获取的类型全路径名反射创建实例
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
获取全路径类名集合
loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)方法加载给定类型的全路径名,通过SpringFactoriesLoader从全部“META-INF/spring.factories”文件中根据给定的type类型获取所有对应的是实现类的全路径类名集合。这里的type就是ApplicationContextInitializer
对用到的全路径类名下的类进行实例化
createSpringFactoriesInstances()该方法将会对上面方法加载的全部ApplicationContextInitializer的实现进行实例化,实际上就是一系列反射创建对象的过程。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
//用来存放实例化完成的类
List<T> instances = new ArrayList(names.size());
Iterator var7 = names.iterator();
//遍历全路径类名
while(var7.hasNext()) {
String name = (String)var7.next();
//根据全路径名获取class对象
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
设置监听器
setListeners设置ApplicationListener监听器。该方法和上面的设置初始化器setInitializers方法的逻辑是一样的,同样是从META-INF/spring.factories文件中获取,只不过上面获取的是ApplicationInitializer类型,而这里获取的是ApplicationListener类型的实例。
spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件中,名为org.springframework.context.ApplicationListener的key对应的value为包括一个监听器。
推断应用程序主类
deduceMainApplicationClass()该方法推断应用程序主类,一般就是Spring boot项目的启动类,也就是调用main方法的类。
运行spring应用实例
run()方法将会运行SpringApplication实例,并且创建并刷新一个新的ApplicationContext。该方法就是Spring boot项目启动的关键方法。
该方法主要有以下步骤:
1、从spring.factories文件中加载运行监听器的实现,比如EventPublishingRunListener
2、执行所有运行监听器实现的starting方法,进行早期的初始化操作,比如,EventPublishingRunListener的starting方法会向之前初始化的所有的ApplicationListener发送一个ApplicationStartingEvent事件,标志着SpringApplication的启动。
3、根据监听器和启动参数准备环境,查找项目的配置文件和激活的profile等信息。
4、打印Banner图标,printBanner()方法。
5、创建spring上下文容器实例
6、准备上下文容器prepareContext(),该方法是核心方法,将会将启动类注入到容器上下文内部的beanfactory中,有了这一步,后面就可以解析启动类的注解和各种配置,进而进行springboot的自动配置
自动配置时,依据@ComponentScan扫描到启动类下所有包下的标有组件注解的类,将其注入到IOC容器中,依据@EnableAutoConfiguration注解下的@Import注解导入AutoConfigurationImportSelector选择器,并使用selectImports()方法,去spring.factories文件中找KV键值对中K为xxxAutoConfiguartion的值,找到要注入IOC容器的类路径,再找出属性K,赋值后注册为Bean,放入IOC容器中。还有框架自带的某些类也会在此期间注册为IOC中的Bean。
如何注册为Bean?
将每个需要注册为Bean的类的信息通过BeanDefinitionRegister接口注册为BeanDefinition到注册表中,再填充BeanDefinitionMap<BeanName,BeanDefinition实现类>,BeanDefinitionNames,存放所有要注册的BeanDefinition名称,后续根据BeanDefinitionNames通过反射获取Bean即可完成Bean的创建。
7、刷新上下文容器refreshContext(),该方法将会加载和解析容器中的bean以及各种配置
8、刷新后处理afterRefresh()。该方法是一个扩展方法,没有提供任何默认的实现,我们自定义的子类可以进行扩展,在afterRefresh()方法中运行callRunners()方法调用runner,即执行容器中的ApplicationRunner和CommandLineRunner类型bean的run方法。这也是一个扩展点,用于执行spring容器完全启动后需要做的逻辑。
9、应用SpringApplicationRunListener监听器的started方法。EventPublishingRunListener将会发出ApplicationStartedEvent事件,表明容器已启动
10、应用SpringApplicationRunListener监听器的running方法。EventPublishingRunListener将会发出ApplicationReadyEvent事件,表明容器已就绪,可以被使用了。