描述:众所周知,springboot以轻量级/简化著称,其可通过Main方法直接运行项目
SpringApplication初始化过程如下
@SpringBootApplication
public class SpringBootDemo81Application {
public static void main(String[] args) {//步骤1
SpringApplication.run(SpringBootDemo81Application.class, args);
}
}
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class> primarySource,
String... args) {//步骤2
return run(new Class>[] { primarySource }, args);
}
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class>[] primarySources,
String[] args) {//步骤3
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(Class>... primarySources) {
this(null, primarySources);//步骤4
}
//步骤5
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//@1
this.webApplicationType = WebApplicationType.deduceFromClasspath();//@2
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));//@3
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//@4
this.mainApplicationClass = deduceMainApplicationClass();//@5
}
如上所需,通过debug源码跟进发现总共执行5个步骤
步骤1 传入SpringBootDemo81Application类,此类是核心应用类,用于构建,args未传入
步骤2 主要是构建SpringApplication 对象,然后调用其run方法
步骤3.4。.5用于构建SpringApplication 对象,详细跟分析步骤5如下
@1设置primarySources 等于传入的Springboot类
@2接着设置当前运行的环境,通过一个枚举判断当前所处的环境状态
/**
* The application should not run as a web application and should not start an
* embedded web server.
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
*/
REACTIVE;
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;
}
@3初始化加载一些参数和配置
private Collection getSpringFactoriesInstances(Class type,
Class>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();//@3-1
// Use names and ensure unique to protect against duplicates
Set names = new LinkedHashSet<>(//@3-2
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);//@3-3
AnnotationAwareOrderComparator.sort(instances);//@3-4
return instances;
}
//@3-1获取当前线程的ClassLoader
//@3-2加载配置如下
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
这段代码主要是通过classLoader加载所以spring相关jar里面META-INF/spring.factories的配置,涉及到工程如下(spring-boot、spring-boot-autoconfigure、spring-boot-devtools)
然后把当前线程的ClassLoader对应资源集合存储到cache缓存中,便于后续使用
接着分析上面//@3-3
@SuppressWarnings("unchecked")
private List createSpringFactoriesInstances(Class type,
Class>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set names) {
List instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
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;
}
之前步骤已经把相关类加载,此处主要是转换实体,加入到集合中
//@3-4对集合进行排序
//@4同3一样进行赋值并加入集合
//@5 获取到Main函数启动所在类
private Class> deduceMainApplicationClass() {
try {
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;
}
获取当前方法的调用栈,找到main函数所在类,并设置到SpringApplication对象的mainApplicationClass属性
总结:
SpringApplication初始化,主要进行了项目包结构配置加载,并获取当前方法的调用栈,找到main函数所在类所运行
作者简介:张程 技术研究
更多文章请关注微信公众号:zachary分解狮 (frankly0423)