SpringBoot 源码分析 - SpringApplication启动流程一
前言
Spring Boot
之所以能那么强大,扩展性那么好,底层还是依赖spring
,他的注解,他的处理器,他的监听器,可以有无数的扩展,废话不多说,当然还是老习惯,要搞懂Spring Boot
的原理,得先从他的初始化开始,然后层层深入,各个击破。其实很多人说看源码比较累,这个不可否认,又是英语,又是代码,又不知道作者想表达什么,只能边看边猜边调试,这也是个学习的过程啊,一个锻炼你学习能力的过程,一开始是很难,但是事件久了你看到别人的源码,你自己就能猜到他可能要做点什么,是怎么做的,这些是你不看源码不可能有的反应,这个就跟小时候为什么经常看小说的人写作都比不看的人要厉害,我是深有体会的。我看到有人说我们不需要看源码,只要会用就好了,那我想说,你不明白原理,你能用的好么,然后又有人会反驳,你开车不用理解车的原理吧,你照样可以开的溜,我想说是的,开车的时候我是个消费者,但是我们程序员写代码的时候是生成者,角色定位要清楚,你写的东西,你自己都不了解,出了问题找别人?4S店给修BUG么
,还不是自己得来,你不看源码,你都不知道内部机制是怎么样,你就用上了,关键你肯定用不好啊。Spring Boot
够简单了吧,跟开车一样,基本不需要什么经验吧,但是我可以说稍微报个错,你根本不知道要怎么办,因为不了解原理,然后你可能就百度啊,猜啊,这不浪费时间么,源码里面都写的很明明白白的,说句不好听的,3年经验
,可能就是百度,CRUD
,写业务逻辑了3年,表面上好像自己踩过很多坑,解决了很多问题,其实很多可能就是源码里,你不熟悉,导致你用不好,问题就多,其实你花一个月看源码,里面都有你要的答案和可能暴露的问题。
初始化基本流程
SpringApplication.run
我们从最简单的例子开始,就这些东西,我们看看SpringApplication.run
到底做了什么事。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
内部还有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);
}
SpringApplication构造方法
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
首先创建一个集合存放我们传进去的类,然后推断应用程序的类型,是SERVLET
,还是REACTIVE
,然后获取很多jar包
下的META-INF/spring.factories
中的org.springframework.context.ApplicationContextInitializer
属性的值和org.springframework.context.ApplicationListener
属性的值,其实他们是接口,他们的值就是其实就是实现类,也就是说要获取这些接口的实现类,来做一些初始化工作,当然里面会做一些筛选,去重。然后推断出main
方法的类,他用了一种很巧妙的方式,抛出一个异常,然后再方法栈里找有main
方法的那个类,具体的细节后面会说。
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;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
WebApplicationType的deduceFromClasspath推断Web应用程序类型
要对照这些类看:
@Deprecated
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext";
/** @deprecated */
@Deprecated
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/** @deprecated */
@Deprecated
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
public static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt";
public static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
这个逻辑我就不讲了,就是排他的,要么REACTIVE
,要么SERVLET
,要么就普通的。具体是根据Class.forName反射
的,而且如果一次不行,还会进行内部类的反射,否没有的话才捕获异常,返回false
。可以看到如果同时有REACTIVE和SERVLET
的相关类,会判定是SERVLET
。
static WebApplicationType deduceFromClasspath() {
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];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
ClassUtils的isPresent是否存在类型
我们接着往下看
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
} catch (IllegalAccessError var3) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);
} catch (Throwable var4) {
return false; //没有就返回false
}
}
ClassUtils的forName
Class.forName
了两次,一次是一般类,一次是内部类,都没有就抛异常。
public static Class<?> forName(String name, @Nullable ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
Assert.notNull(name, "Name must not be null");
Class<?> clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
clazz = (Class)commonClassCache.get(name);
}
if (clazz != null) {
return clazz;
} else {
Class elementClass;
String elementName;
if (name.endsWith("[]")) {
elementName = name.substring(0, name.length() - "[]".length());
elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith("[L") && name.endsWith(";")) {
elementName = name.substring("[L".length(), name.length() - 1);
elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith("[")) {
elementName = name.substring("[".length());
elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
} else {
ClassLoader clToUse = classLoader;
if (classLoader == null) {
clToUse = getDefaultClassLoader();
}
try {
return Class.forName(name, false, clToUse);
} catch (ClassNotFoundException var9) {
int lastDotIndex = name.lastIndexOf(46);
if (lastDotIndex != -1) {
String nestedClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);
try {
return Class.forName(nestedClassName, false, clToUse);
} catch (ClassNotFoundException var8) {
}
}
throw var9;
}
}
}
}