道阻且长,行则将至。
入口代码如下:
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);
}
初始化流程的入口就是创建SpringApplication的对象:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
(1)、设置resourceLoader及primarySources。
(2)、需要确定WebApplicationType,
该类型共有三个实例
/**
* 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;
决定使用哪个类型,是通过:判断classPath下是否存在javax.servlet.Servlet,如果不存在则返回NONE,如果存在org.springframework.web.servlet.DispatcherServlet那么就使用SERVLET。
而判断是否存在该类型则是通过如下代码实现的:主要是使用forName
public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {
Assert.notNull(name, "Name must not be null");
Class<?> clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
clazz = commonClassCache.get(name);
}
if (clazz != null) {
return clazz;
}
// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class<?> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[Ljava.lang.String;" style arrays
if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
Class<?> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[[I" or "[[Ljava.lang.String;" style arrays
if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
Class<?> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
ClassLoader clToUse = classLoader;
if (clToUse == null) {
clToUse = getDefaultClassLoader();
}
try {
return Class.forName(name, false, clToUse);
}
catch (ClassNotFoundException ex) {
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
if (lastDotIndex != -1) {
String innerClassName =
name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
try {
return Class.forName(innerClassName, false, clToUse);
}
catch (ClassNotFoundException ex2) {
// Swallow - let original exception get through
}
}
throw ex;
}
}
ps:以上代码是ClassUtils类。该类作为工具类,在class标记的时候使用了abstract,禁止被实例化。
最终还是使用了Class.forName来尝试加载类,如果返回为空,那么就是这个类找不到。这里还会尝试加载内部类,如果内部类也加载不到那么再抛出ClassNotFoundException。
此时如果Springboot中内置了DispatcherServlet的话,WebApplicationType就是SERVLET。
** (3)、设置ApplicationContextInitializer和ApplicationListener到SpringApplication中:
具体的操作是依赖下面的代码来完成的。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 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;
}
首先获取classPath下面的所有的指定类型的类名,然后根据类名创建实例,并返回,设置到SpringApplication中。这里主要是依赖SpringFactoriesLoader类实现的。该类也是个工具类,被声明为final。构造函数被声明为private,禁止实例化。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> 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 factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
此处使用的是java的SPI机制。此处默认使用的ClassLoader是当前线程的:
@Nullable
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
通过classLoader来获取路径下面的资源。此处不再详细描述。
找到所有定义在META-INF/spring.factories中的ApplicationContextInitializer和ApplicationListener实现类。并创建实例,设置到SpringApplication中。此处使用了BeanUtils和ReflectionUtils来实例化对象。
Springboot默认加载的ApplicationContextInitializer和ApplicationListener的实现类如下:
org.springframework.boot.ClearCachesApplicationListener,\清理通过反射获取的Method和Field时存储的缓存
org.springframework.boot.builder.ParentContextCloserApplicationListener,\监听父容器关闭事件
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\该类还是个EnvironmentPostProcessor,处理VCAP。
org.springframework.boot.context.FileEncodingApplicationListener,\判断系统的文件编码是否与spring.mandatory-file-encoding一致,不一致会报错。
org.springframework.boot.context.config.AnsiOutputApplicationListener,\忽略
org.springframework.boot.context.config.ConfigFileApplicationListener,\加载application.properties或者其他文件后面详细介绍
org.springframework.boot.context.config.DelegatingApplicationListener,\通过该类将已经加载的listener注册到SimpleApplicationEventMulticaster中
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\通知检查完成的listener
org.springframework.boot.context.logging.LoggingApplicationListener,\配置logging类
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener忽略
org.springframework.boot.autoconfigure.BackgroundPreinitializer忽略
ApplicationContextInitializer的实现类如下:
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener,
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
后会面会重点介绍ConfigFileApplicationListener
** (4)、推断Main方法所在的类:
这里面做的操作就是抛出一个异常,然后通过异常栈来判断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;
}
至此,SpringApplication就初始化完成。