NOTE
2.63 启动原理
1. 启动SpringBoot 我们直接从SpringBoot启动的这行代码开始 忽略简单的嵌套调用,最终调用的代码如下: 首先会使用主配置类Application.class创建SpringApplication,然后传入启动参数args启动 2. 创建SpringApplication的过程 我
这是历史学习笔记,可能存在过时或不完整的理解。
1. 启动SpringBoot
我们直接从SpringBoot启动的这行代码开始
SpringApplication.run(Application.class, args)
忽略简单的嵌套调用,最终调用的代码如下:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
//(Application.class, args)
return new SpringApplication(sources).run(args);
}
首先会使用主配置类Application.class创建SpringApplication,然后传入启动参数args启动
2. 创建SpringApplication的过程
我们首先看SpringApplicaiton是怎么创建的
public SpringApplication(Object... sources) {
//创建的时候初始化
initialize(sources);
}
private void initialize(Object[] sources) {
//保存配置类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断是否web环境并保存
this.webEnvironment = deduceWebEnvironment();
//在类路径下META-INF/spring.factories找到所有的ApplicationContextInitializer类,保存起来
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//在类路径下META-INF/spring.factories找到所有的ApplicationListener类,保存起来
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//判断主配置类(有main方法的)并保存
this.mainApplicationClass = deduceMainApplicationClass();
}
初始化主要分为5个步骤
2.1. 判断是否web环境
private boolean deduceWebEnvironment() {
//private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
// "org.springframework.web.context.ConfigurableWebApplicationContext" }
//如果能加载以上任意一个就是web环境
for (String className : WEB_ENVIRONMENT_CLASSES) {
//加载失败返回false
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
2.2. 加载所有的ApplicationContextInitializer
2.2.1. 首先找到所有的ApplicationContextInitializer类,分为三个步骤
- 找到所有的ApplicationContextInitializer类的名字
- 实例化
- 排序
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//找到所有的ApplicationContextInitializer类的名字
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
2.2.1.1. 那么是怎么找到名字的呢
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//使用当前线程的类加载器加载类路径resources目录下的所有META-INF/spring.factories文件
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
//每个文件解析成Properties类实例
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
//找到名字为org.springframework.context.ApplicationContextInitializer的属性对应的value
String propertyValue = properties.getProperty(factoryClassName);
//分割保存到list中返回
for (String factoryName : StringUtils.commaDelimitedListToStringArray(propertyValue)) {
result.add(factoryName.trim());
}
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
2.2.1.2. 创建实例的操作其实就是通过反射调用构造方法
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
//通过类名和类加载器创建class实例
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
//获取class的构造方法
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
//通过构造方法和参数实例化
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
2.2然后保存到initializers中
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers.addAll(initializers);
}
2.3. 加载所有的ApplicationListener
逻辑同2
2.4. 保存主配置类
private Class<?> deduceMainApplicationClass() {
try {
//遍历调用栈的方法名,有main的就是主配置类
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
2.5. 运行SpringBoot Application
public ConfigurableApplicationContext run(String... args) {
//创建计时器,并启动
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//加载类路径下所有resources目录下的META-INF/spring.factories文件
//创建名字为org.springframework.boot.SpringApplicationRunListener的bean,保存到listeners
SpringApplicationRunListeners listeners = getRunListeners(args);
//调用所有SpringApplicationRunListener的starting方法
//其实就是通过广播器广播时间,最后回调listener的onApplicationEvent方法
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//创建环境bean
//web的话创建StandardServletEnvironment,否则StandardEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//打印banner
//如果关闭了那就不打印
//设置了log模式,使用logger打印
//默认console模式,使用System.out输出到控制台
//加载image。banner.image.location属性,没有的话resouces目录下的banner.gif等
//加载text。banner.location属性。
//以上没有的话使用默认的SpringBootBanner
//调用banner的printBanner方法
//打印字符串
//打印版本信息
Banner printedBanner = printBanner(environment);
//创建applicaiton context
//web的话创建AnnotationConfigEmbeddedWebApplicationContext
//否则AnnotationConfigApplicationContext
context = createApplicationContext();
//创建FailureAnalyzers
//加载类路径下所有resources目录下的META-INF/spring.factories文件
//创建所有名字为org.springframework.boot.diagnostics.FailureAnalyzer的bean,保存到analyzers中
//如果是BeanFactoryAware类型的,那么注入BeanFactory
analyzers = new FailureAnalyzers(context);
//准备容器
//调用所有ApplicationContextInitializer的initialize方法
//调用所有SpringApplicationRunListener的contextPrepared方法
//打印启动信息
//打印profile信息
//注册主配置类的BeanDefinition
//调用所有SpringApplicationRunListener的contextLoaded方法
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新容器。refresh
refreshContext(context);
//完成刷新容器后。
//调用ApplicationRunner、CommandRunnner的run方法
//调用SpringApplicationRunListener的finished方法
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
//停止计时器
stopWatch.stop();
//打印启动时长
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
可以看出调用了spring的refresh方法,参考1.创建spring容器.md