使用Springboot框架进行应用开发,方便得一P,但是不能仅满足于会用,而要了解其背后的运行机制,这样才能在框架出现报错时,不慌不乱,谈笑间消灭掉Bug。
而源代码是框架最好的导师,不废话,开讲!
要启动Springboot应用,需要配置启动类,如下:
@SpringbootApplication
public class Application{
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
可以看到,关键在于SpringApplication的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);
}
本质上是将启动类的Class包装成Class[]作为primarySources,然后传给SpringApplication的构造函数创建其实例,最后再调用SpringApplication实例的run方法完成应用的启动。
由于Springboot Application启动涉及的东西太多,我们分多篇来讲。第1篇先讲一讲SpringApplication的创建过程。
点进去SpringApplication的构造函数:
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.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = this.deduceWebApplicationType();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
需要关注的有deduceWebApplicationType、setInitializers、setListeners、mainApplicationClass这4个方法。
deduceWebApplicationType
该方法主要用于推断应用类型。
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null)) {
return WebApplicationType.REACTIVE;
} else {
String[] var1 = WEB_ENVIRONMENT_CLASSES;
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String className = var1[var3];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
}
- 如果Classpath中存在org.springframework.web.reactive.DispatcherHandler的继承类且不存在org.springframework.web.servlet.DispatcherServlet的继承类,则返回该应用类型为REACTIVE;
- 继续判断Classpath中javax.servlet.Servlet或org.springframework.web.context.ConfigurableWebApplicationContext的继承类是否存在,只要1个不存在,则返回该应用类型为NONE;
- 否则返回该应用类型为SERVLET。
deduceMainApplicationClass
该方法主要用于推断启动类Class。
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
;
}
return null;
}
基本原理是创建1个RuntimeException实例,然后获取其堆栈信息,遍历各栈帧的方法名,若该栈帧的方法名为"main",则返回该栈帧所在的Class。
setInitializers和setListeners
2种方法分别用于初始化所有ApplicationContextInitializer和ApplicationListener的实例,底层均调用的getSpringFactoriesInstances方法,下面具体看看getSpringFactoriesInstances的实现:
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) {
// 获取Class加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 获取该Type所有继承类的Class Name
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据Class Name通过反射创建各继承类的实例
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 根据@Order注解排序后返回
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
先看一下loadFactoryNames方法:
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result.addAll((String)entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}
loadFactoryNames方法先从缓存中获取,若缓存中不存在,则扫描ClassPath中所有的"META-INF/spring.factories"加载,更新到缓存,然后返回结果。
拿到各继承类的className信息后,就可以通过createSpringFactoriesInstances方法来构建实例。
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();
try {
// 通过name加载Class
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
// 获取Class的构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 反射创建该Class的实例
T instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
Tip: 这里有2个工具类:ClassUtils和BeanUtils,有兴趣的可以看一下。
实践一把,构建一个空Springboot的Web项目,然后在SpringApplication的run方法开头打个断点,Debug运行:
1.jpg 2.jpg 3.jpg可以看到,SpringApplication已创建,各项属性也已初始化。
OK,SpringApplication创建的源码已跟完,下篇开始run方法的源码跟读。
网友评论