福兮祸所伏, 祸兮福所倚
入口:
public class BootStudyApplication {
public static void main(String[] args) {
SpringApplication.run(BootStudyApplication.class, args);
}
}
SpringApplication.run(BootStudyApplication.class, args);
debug进入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);
}
这个方法又调用了下面的run 进行两个操作 new SpringApplication然后调用run方法。其中primarySources是数组,也就是说可以有多个, 一个个来说 首选new SpringApplication debug 进入new SpringApplication里面
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 = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
进行了一系列的初始化赋值
首先来看这里的
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
咱们传入的主类 会以LinkedHashSet集合被传入SpringApplication 中 再来看这句 非常重要
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
首先用调用的getSpringFactoriesInstances方法 参数为ApplicationContextInitializer.class ApplicationContextInitializer :spring容器刷新之前(就是扫描咱们程序注入ioc容器的类,包等等)执行的一个回调函数。 进入getSpringFactoriesInstances 调用了getSpringFactoriesInstances方法
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;
}
public ClassLoader getClassLoader() {
return this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader();
}
type:ApplicationContextInitializer (记好了), 首先是得到默认的类加载器 然后通过springFactoriesLoader.loadFactoryName去加载jar下META-INF下的spring.factories spring.factories 里面有什么东西自己看下 具体去这里干嘛 看下面代码
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
首先等到ApplicationContextInitializer的全类名 传入loadSpringFactories方法
这里面通过类加载器去加载的 然后返回一个url集合 然后遍历 放在result里面返回
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();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
再通过
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
返回list 这点可能有点绕 解释下:就是创建SpringApplication实例的时候 会去咱们导入的jar下META-INF下的
spring.factories找资源 所有的全类名 比如 ApplicationContextInitializer的全类名 找完之后 会以map的形式返回
key是就是全类名 value就是 对应的一个个容器 然后loadSpringFactories出来之后 再调用map的getOrDefault方法
保留ApplicationContextInitializer全类名对应的一个个容器 最后得到一个 Set<String>
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
根据得到Set<String> 去创建一个个的实例 并且返回 赋值给SpringApplication实例的Initializer属性 到这ApplicationContextInitializer已经结束了。休息下 咱们接着走
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
嗯哼? 这个编码风格怎么那么熟悉 对 不错 和
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); 一模一样
那就不解释了 感觉聪明的你们都可以理解了。
下面还有一句呢
this.mainApplicationClass = this.deduceMainApplicationClass();
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;
}
这个方法意思就是由于
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
primarySources参数是数组 也就是说我们传入主类的时候 可以传多个 那springboot怎么确定是哪个是老大呢 那他就做了一下判断 有main函数的就是老大
好了 关于new SpringApplication已经说了 剩下个执行run方法 预知后事如何 请看下篇分解
如果有帮到你 请点个赞或者关注点吧 老铁