在上一章學習了spring boot 2.0啟動的大概流程以后,今天我們來深挖一下SpringApplication實例變量的run函數。
先把這段run函數的代碼貼出來:
/**
* 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) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
我們先來分析其中的第一個關鍵代碼:SpringApplicationRunListeners listeners = getRunListeners(args);
這行代碼是獲取監聽器,我們先跳轉到getRunListeners中看一下:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
在這段代碼中,我們看到獲取監聽器,是new出來了一個SpringApplicationRunListeners實例並返回。
再次跳轉到SpringApplicationRunListeners的構造函數中,看到一下發生了什么:
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList(listeners);
}
在這個構造函數里,我們看到其只是把接收到的listeners參數,保存到實例變量里,沒有過多的操作。
所以,重點是在listeners參數這里,而listeners是通過getSpringFactoriesInstances創建出來的,來看一下getSpringFactoriesInstances函數做了什么事情吧:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
在這里我們看到,首先創建了一個classloader,然后用SpringFactoriesLoader.loadFactoryNames(type, classLoader),加載了SpringFactoriesLoader列表。我們來看一下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 result = (MultiValueMap)cache.get(classLoader);
if(result != null) {
return result;
} else {
try {
Enumeration ex = classLoader != null?
classLoader.getResources("META-INF/spring.factories")
:ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result1 = new LinkedMultiValueMap();
while(ex.hasMoreElements()) {
URL url = (URL)ex.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 factoryClassNames = Arrays.asList(StringUtils
.commaDelimitedListToStringArray((String)entry.getValue()));
result1.addAll((String)entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result1);
return result1;
} catch (IOException var9) {
throw new IllegalArgumentException(
"Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}
通過這里我們看到了其首先加載了META-INF/spring.factories這個配置文件下的所有資源,並放入緩存,然后再獲取了org.springframework.context.ApplicationListener定義的資源列表。
小發現:在這里我們發現spring boot自動裝配文件的位置。
獲取到META-INF/spring.factories這個配置文件下的資源名稱列表以后,通過createSpringFactoriesInstances函數創建了SpringFactories的實例。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
ArrayList instances = new ArrayList(names.size());
Iterator var7 = names.iterator();
while(var7.hasNext()) {
String name = (String)var7.next();
try {
Class ex = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, ex);
Constructor constructor = ex.getDeclaredConstructor(parameterTypes);
Object instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name,
var12);
}
}
return instances;
}
通過上面的這些代碼流轉,我們大概搞清楚了listeners是怎么創建出來的。
然后調用了listeners的starting方法。我們先大概地看一下EventPublishingRunListener里面的starting的實現:
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application,
this.args));
}
關鍵代碼在這里:
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null?eventType:this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if(executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if(errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
}
}
在上面代碼中我們看到,starting就是拿到META-INF/spring.factories中定義的資源的實例以后,然后再創建一個線程去啟動起來。
通過上面的這些代碼我們知道了spring boot會獲取META-INF/spring.factories中的資源,並創建這些資源的實例(listeners監聽器),然后為每一個監聽器創建一個線程啟動起來。
篇幅有限, 今天就寫到這里吧。有希望一起學習spring boot 2.0源碼的同學可以關注我,跟我一起分析spring boot 2.0 源碼的實現方式。