基于Spring Boot 2.2.5 Release版本。
SpringApplication
首先调用静态方法
SpringApplication.run 返回ConfigurableApplicationContext 应用上下文。
//这个run 静态方法其实是调用下面的那个,不过他把单个primarySource 转为了数组
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
//根据primarySources 构建SpringApplication对象。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
然后 构建SpringApplication对象
构建方法有2个,
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//将primarySources 数组转化为LinkedHashSet
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//确定应用的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取初始化类集合
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取监听类集合
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//根据调用栈推测main 类。
this.mainApplicationClass = deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath
//推测程序类型
static WebApplicationType deduceFromClasspath() {
//org.springframework.web.reactive.DispatcherHandler 存在,
//并且org.springframework.web.servlet.DispatcherServlet 不存在,
// 并且org.glassfish.jersey.servlet.ServletContainer 不存在
//则应用是WEBFLUX类型的
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
//查找"javax.servlet.Servlet" 和"org.springframework.web.context.ConfigurableWebApplicationContext"
//如果任意类不存在则应用类型为NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
//定义为SERVLET 类型
return WebApplicationType.SERVLET;
}
尝试创建MVC 应用和 WEBFLUX应用观察。
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) {
//获取类加载实例
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//使用SpringFactoriesLoader依据类型加载
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,会加载资源META-INF/spring.factories文件中记录的所有类。这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spirng容器中。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//查看缓存
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
//FACTORIES_RESOURCE_LOCATION 就背定义为META-INF/spring.factories
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
//将spring.factories 中的内容,以接口名/抽象类名/具体类名为key,具体实现类名为Value存放在result中。
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());
}
}
}
//以classLoader为key, result为value存放在内存中。
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
loadSpringFactories 就是加载
- spring-boot-2.2.5.RELEASE.jar!/META-INF/spring.factories
- spring-boot-autoconfigure-2.2.5.RELEASE.jar!/META-INF/spring.factories
- spring-beans-5.2.4.RELEASE.jar!/META-INF/spring.factories
- mybatis-spring-boot-autoconfigure-2.1.1.jar!/META-INF/spring.factories
- pagehelper-spring-boot-autoconfigure-1.2.5.jar!/META-INF/spring.factories
内容到result。
createSpringFactoriesInstances
//拿到类名,实例化对象
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
SpringApplication构建方法流程:
1.初始化一些属性的初始值。
2.推断应用的类型,从是否引用WEBFLUX 和MVC来判断的应用类型。
3.依靠getSpringFactoriesInstances方法获取实现了ApplicationContextInitializer.class接口的所有实例对象。
- getSpringFactoriesInstances 先获取classload,再通过SpringFactoriesLoader获取ApplicationContextInitializer.class对应的实现类
- 再通过createSpringFactoriesInstances把实现类实例化。
4.再依靠getSpringFactoriesInstances方法方法实现了ApplicationListener.class接口的所有实例对象。
5.再通过deduceMainApplicationClass推测是哪个类