public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
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);
}
Spring Boot主程序main方法只做了两件事
一、创建SpringApplication实体类对象
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//resourceLoader = null
//primarySources = DemoApplication.class
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
/**
* WebApplicationType.deduceFromClasspath()就是根据各种条件判断确定当前服务器类型
* 该方法主要调用以下方法进行判断
* ClassUtils.isPresent(String className, @Nullable ClassLoader classLoader)
* className 全类名
* classLoader 类加载器
* 判断能否从classLoader(类加载器)中获取与className(全类名)相对应的Class类
* (方法内部判断不止一个判断,但大致是这个意思,有兴趣可以自己研究)
*/
this.webApplicationType = WebApplicationType.deduceFromClasspath();
/**
* 调用了两个方法
* 1. getSpringFactoriesInstances(ApplicationContextInitializer.class)
* 这个方法很重要,在下面有单独说明,看完再继续
* 从所有META-INF/spring.factories文件中获取与ApplicationContextInitializer.class全类名相同Key的对应Value值的实体类对象
*
* 2. setInitializers
* 为this.initializers初始化并赋值
*/
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
/**
* 和上面方法是一样的
*/
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
/**
* 保存main(Spring Boot主程序)所在类的Class对象
*/
this.mainApplicationClass = deduceMainApplicationClass();
}
1. getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
/**
* getClassLoader()返回本地类加载器,如果为空返回默认类加载器(默认返回的是线程上下文类加载器)
*/
ClassLoader classLoader = getClassLoader();
/**
* SpringFactoriesLoader.loadFactoryNames(type, classLoader)
* 这个方法很重要,在下面有单独说明,看完再继续
*/
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
/**
* createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)
* 这个方法就是根据names(全类名)和classLoader(类加载器)来获取names对应的实例,反射机制
*/
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
/**
* 返回获取到的实体类集合
*
* 总结:
* 这个方法就是从所有的META-INF/spring.factories文件中获取所传参type的全类名对应的Value,
* 并把获取到的Value根据反射机制实体化,最后把实体化的对象集合返回
*/
return instances;
}
2. loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
/**
* 获取Class对象的全类名
*/
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
/**
* 从cache中获取classLoader相对就的value值
*/
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 properties = PropertiesLoaderUtils.loadProperties(resource);
/**
* 获取配置数据的迭代器
*/
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
/**
* 获取到一份配置数据的Key-Value键值对
*/
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
/**
* Value默认是数组形式,遍历
*/
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
/**
* 这个添加方法其实是把数据添加到了该实体类的Map<K, List<V>>类型的成员变量中
*/
result.add(factoryClassName, factoryName.trim());
}
}
}
/**
* 所有META-INF/spring.factories配置文件下的数据都被保存起来
*/
cache.put(classLoader, result);
/**
* 返回获取到的LinkedMultiValueMap数据(就是数据集,内部封装了Map,Value值是List集合)
*
* 总结:
* 这个方法就是使用类加载器获取所有META-INF/spring.factories文件地址,并把所有文件以Key-Value的形式进行解析
* 最后保存解析出来的数据并返回
*/
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
二、调用run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
/**
* getRunListeners(args)方法内主要调用了getSpringFactoriesInstances方法,并用返回参数创建SpringApplicationRunListeners
* getSpringFactoriesInstances上面说过,跳过
*/
SpringApplicationRunListeners listeners = getRunListeners(args);
/**
* 使listeners对象变量this.listeners中的每个对象调用starting()方法(就是启动并初始化对象)
*/
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/**
* 配置环境并回调environmentPrepared方法
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
/**
* 创建IOC容器
*/
context = createApplicationContext();
/**
* getSpringFactoriesInstances这个方法上面说过,略
*/
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
/**
* 准备上下文,这个方法很重要下面单独说明
*/
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
/**
* IOC容器初始化,加载IOC容器中所有组件
*/
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
/**
* 所有SpringApplicationRunListener回调started方法
*/
listeners.started(context);
/**
* 从容器中获取所有的ApplicationRunner和CommandLineRunner对象并回调run方法
*/
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
/**
* 所有SpringApplicationRunListener对象回调running方法
*/
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
1. prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
/**
* 为IOC容器设置环境
*/
context.setEnvironment(environment);
/**
* IOC容器后置处理,根据需要设置其它处理
*/
postProcessApplicationContext(context);
/**
* 所有ApplicationContextInitializer对象回调initialize方法
*/
applyInitializers(context);
/**
* 所有SpringApplicationRunListener对象回调contextPrepared方法
*/
listeners.contextPrepared(context);
/**
* 日志记录相关设置
*/
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
/**
* 获取Bean工厂并根据条件添加组件
*/
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
/**
* 将bean加载到应用程序上下文中
*/
load(context, sources.toArray(new Object[0]));
/**
* 所有SpringApplicationRunListener对象回调contextLoaded方法
*/
listeners.contextLoaded(context);
}