本文采用的是springboot 1.5.9版本,没有使用2.X版本。
pom.xml里加入:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
建立一个springboot项目后,自动会新建一个项目的Application类
@SpringBootApplication
public class SpringbootCodeReadApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCodeReadApplication.class, args);
}
}
本篇的内容便是对这个run方法进行详细讲解。
顺着run进入源码,
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
这里的参数sources就是我们的启动类SpringbootCodeReadApplication,可以看到这里new了类SpringApplication,因此接下来看看该类的构造方法。
public SpringApplication(Object... sources) {
...忽略
this.initialize(sources);
}
在这里忽略了不重要的代码,只保留了构造方法中最重要的initialize()方法。
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//1.判断当前环境是否是web环境,判断方法是检查classPath中是否
//存在javax.servlet.Servlet类 &&存在ConfigurableWebApplicationContext类
this.webEnvironment = this.deduceWebEnvironment();
//2.设置“META-INF/spring.factories”中ApplicationContextInitializer相关的Inializers到SpringApplication中
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//3.设置“META-INF/spring.factories”中ApplicationListener相关的Listeners到SpringApplication中
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//4.找到main方法所在的类。
this.mainApplicationClass = this.deduceMainApplicationClass();
}
第一步的作用是判断当前环境是否是web环境,因为本文在pom.xml中设置了spring-boot-starter-web
,所以是web环境。
第二步和第三步是在SpringApplication中设置Inializers和ApplicationListener,两者都用到了getSpringFactoriesInstances()方法,接下来对这个方法进行讲解:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//1.获取当前线程classLoader,用于后续通过反射加载类
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//2.重点:获得type对应的类的名称(去重),下面会详细介绍
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//3.在第二步获得类名后,使用反射来创建实例
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//4.排序,不详细介绍
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
其中最关键的是第二步中的loadFactoryNames()方法:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
//1.获取工厂的类名
String factoryClassName = factoryClass.getName();
try {
//2.从"META-INF/spring.factories"中获得urls
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
//3. 遍历 urls
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
//4.获取url对应的properties
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
//5.从properties获取factoryClassName类包含的类名
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
...略
}
}
这段代码直接去看比较困难,需要结合debug来进行介绍。
从下图可以看到,假设我们正在获取ApplicationContextInitializer的Initializer,那么此时factoryClassName为org.springframework.context.ApplicationContextInitializer
。中可以看到,urls中的其中一个元素为绝对路径XXX/spring.factories。
可以看到,spring.factories中一共含有七个大类,其中绿框就是我们本次要找的ApplicationContextInitializer,同时,从红框中可以看到,ApplicationContextInitializer由四个小类组成。记住七和四这两个数字。
现在我们回到代码中,第四步获得properties实际上就是上面提到的七个大类极其对应的小类。
因为factoryClassName=ApplicationContextInitializer,所以第五步中factoryClassNames 为上面提到的四个小类。并在加到list后返回。
现在回到getSpringFactoriesInstances()方法,该方法还剩下第三步没讲:
第三步实际上就是使用java的反射机制,创建刚刚获得的类名对应的实例,代码如下:
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 {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
到此,getSpringFactoriesInstances()方法介绍完毕,现在我们回到最初的initialize()方法:
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//1.判断当前环境是否是web环境,判断方法是检查classPath中是否
//存在javax.servlet.Servlet类 &&存在ConfigurableWebApplicationContext类
this.webEnvironment = this.deduceWebEnvironment();
//2.设置“META-INF/spring.factories”中ApplicationContextInitializer相关的Inializers到SpringApplication中
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//3.设置“META-INF/spring.factories”中ApplicationListener相关的Listeners到SpringApplication中
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//4.找到main方法所在的类。
this.mainApplicationClass = this.deduceMainApplicationClass();
}
第三步与第二步是相同的操作,因此接下来只剩第四步没有介绍。
第四步其实就是找到main()方法所在的类,在本文中就是启动类SpringbootCodeReadApplication,代码如下:
private Class<?> deduceMainApplicationClass() {
try {
//1.从RuntimeException中获得所有堆栈
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
//找到main方法所在的类。
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
}
return null;
}
至此,initialize()讲解完毕。
run方法是本文最核心的篇章,并且该方法内容量很大,需要一点一点耐心分析。
首先,对整个run方法的具体步骤进行注释,一共有10几步,如下代码所示:
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//1. 初始化stopwatch,用于计时,本文不详细讲解
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//2. 设置系统属性java.awt.handless为true,
//该属性表示系统可以在缺少显示设备、键盘或鼠标这些外设的情况下进行使用,本文不详细讲解。
configureHeadlessProperty();
//3. 获得runListeners,并启动(调用listener的onApplicationEvent方法)
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//4. 创建DefaultApplicationArguments(该类含有args),然后调用prepareEnvironment()
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//5. 打印banner
Banner printedBanner = printBanner(environment);
//6. 创建Springboot的上下文ApplicationContext
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//7. prepareContext
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//8. spring最出名的refresh()方法
refreshContext(context);
//9. TODO
afterRefresh(context, applicationArguments);
//10. TODO
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
//11. 处理异常
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
现在对第三步进行分析,先进入getRunListeners(args):
private SpringApplicationRunListeners getRunListeners(String[] args) {
//3.1 创建Class数组,该数组用于getSpringFactoriesInstances()方法中反射时找到对应的构造函数。
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 3.2 调用getSpringFactoriesInstances()获得SpringApplicationRunListener相关的listeners的实例
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
第一步先跳过,在第二步中可以看到,getRunListeners(args)实际上是通过调用getSpringFactoriesInstances()方法来获取SpringApplicationRunListener相关的listeners实例,。
getSpringFactoriesInstances()方法在上一章“初始化initialize() ”的已详细介绍,因此在这里不再重复介绍,只给出获得的结果:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
可以看到,本次getSpringFactoriesInstances()获取到的是EventPublishingRunListener类。
现在我们回到第一步,之所以要创建{ SpringApplication.class, String[].class }
这样一个数组,是因为反射时需要找到对应的构造方法Constructor,在下面的代码中可以看到,EventPublishingRunListener类拥有参数为(SpringApplication.class, String[].class)的构造方法,因此按该构造方法生成实例。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
...略
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//注意:这里会将initialize方法中存放的listeners提取出来
//然后存放到this.initialMulticaster中
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
...略
}
该构造方法有需要注意的点:这里会将initialize()方法中存放的listeners提取出来然后存放到this.initialMulticaster里。
接下来介绍启动监听器listeners.starting();
:
一直往下走,每一个监听器都会调用下面的代码:
@Override
@SuppressWarnings("deprecation")
public void starting() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
接着:
public void multicastEvent(ApplicationEvent event) {
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
resolveDefaultEventType()方法不详细介绍,该方法的作用是创建一个ResolvableType类。接下来开始看multicastEvent()方法:
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
//上面已经创建了,所以type不会为null
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//1. getApplicationListeners(event,type)是将EventPublishingRunListener中存放的listeners取出来,
// 接着只保留将该event对应的listeners(判断方法的具体实现过于复杂,知道功能即可)并放到一个LinkedList中
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//遍历listner,每个listener都创建线程,并调用invokeListener()
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
顺着invokeListener走,最后会到下面的代码,可以看到,开启listener其实也就是调用 onApplicationEvent(event) 方法:
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
...略
}
}
总结一下:获得runListeners是从spring.factories文件中获得SpringApplicationRunListener相关的listener,启动实际上是建立一个线程池,并调用onApplicationEvent(event)方法。