springboot 启动分析一
SpringApplition 类
类简介
该类能够通过一个简单的java方法(run方法),引导和加载一个spring应用程序,默认情况下SpringApplication类将执行以下几步去引导你的应用程序
-
根据你的classpath创建一个合适的应用上下文实例(ApplicationContext)
-
创建一个CommandLinePropertySource实例接收并暴露命令行参数作为spring的属性(springboot应用程序引导是在主方法中,而CommandLinePropertySource实例就是接收主方法传入的各种spring参数并暴露给SpringApplication)
-
刷新应用上下文,加载所有的单例bean
-
最后触发任何的CommandLineRunner bean
构造方法
SpringApplication 提供了两个构造方法
/**
* 创建一个新的SpringApplication 实例,应用上下文将会从定义的primarySources加载bean定义,
* 可以在调用SpringApplication实例的run方法之前定制该实例
* @param primarySources 主要的bean 资源
*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
- 可以看到这里转而调用了重载的构造
/**
* 创建一个新的SpringApplication 实例,应用上下文将会从定义的primarySources加载bean定义,
* 可以在调用SpringApplication实例的run方法之前定制该实例
*
* @param resourceLoader 资源加载器
* @param primarySources 主要的bean 资源
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 初始化资源加载器,这里默认是null
this.resourceLoader = resourceLoader;
// 断言主资源不是null
Assert.notNull(primarySources, "PrimarySources must not be null");
// 将主资源放入SpringApplication 对象的primarySources 属性中
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用类型
this.webApplicationType = deduceWebApplicationType();
// 根据项目下META-INF文件夹下的spring.factories配置文件做一些应用上下文bean的初始化工作
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 根据项目下META-INF文件夹下的spring.factories配置文件做一些应用监听器bean的初始化工作
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 初始化主要的应用的Class类型,是遍历取出当前线程的堆栈信息中包含有“main”方法的class类
this.mainApplicationClass = deduceMainApplicationClass();
}
- 推断应用类型的方法deduceWebApplicationType()
/**
* 根据classpath下是否存在指定的类,推断出应用的类型
*
* @return org.springframework.boot.WebApplicationType 应用类型枚举类
* @date 9:32 2018/9/24
*/
private WebApplicationType deduceWebApplicationType() {
// 判断 存在REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework.web.reactive.DispatcherHandler";
// 且不存在 MVC_WEB_ENVIRONMENT_CLASS = "org.springframework.web.servlet.DispatcherServlet";
// 且不存在 JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";
// 就是 REACTIVE 类型
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 判断只要classpath不存在二者任意之一,
// String[] WEB_ENVIRONMENT_CLASSES = {"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
// 就认为是NONE 类型的应用 即非Web型应用(Standard型)
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 以上两者都不是,返回SERVLET 类型应用
return WebApplicationType.SERVLET;
}
- 获取spring工厂类实例
/**
* 根据传入的class类型从spring.factories配置文件中获取该class类型作为key对应的class集合,
* 再利用反射获取对应的实例
*
* @param type 传入的key (class类型)
* @return java.util.Collection<T> value实例集合
* @date 9:59 2018/9/24
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[]{});
}
/**
* 根据class类型的type参数,从spring.factories 配置文件中获取对应的value集合
* 利用反射初始化value 对象返回
*
* @param type spring.factories中定义的各种key
* @param parameterTypes 参数类型
* @param args 参数
* @return java.util.Collection<T>
* @date 10:05 2018/9/24
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
// 获取当前线程的classloader
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 使用set不可重复来收集type对应的value集合中含有的名字,避免重复实例化对象
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 利用反射实例化对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
- 重点看一下,SpringFactoriesLoader.loadFactoryNames(type, classLoader) 这个方法
SpringFactoriesLoader 类定义为final型,意味着不能被继承,是一个spring内部使用的通用工厂加载器,用于读取spring.factories文件,类内部定义了一个常量,指定工厂资源文件位置
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
- 继续往下
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
// 获取传入的工厂类的名字
String factoryClassName = factoryClass.getName();
// 加载工厂配置文件中的所有工厂类存入map中, key为工厂类型,value为所有的工厂类集合,再根据指定的工厂类名,获取指定的value集合
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
// 其中用到了缓存,指定classloader只加载一次spring.factories文件中
// 的所有配置,以key,value的形式存入一个map中,并缓存起来
// 缓存的方式是以classloader为key,整个map为value
// 缓存定义 private static final Map<ClassLoader, MultiValueMap<String,String>>
// cache = new ConcurrentReferenceHashMap<>();
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//首先从缓存中查询,该classloader是否已有缓存
MultiValueMap<String, String> result = cache.get(classLoader);
// 有缓存,直接返回
if (result != null) {
return result;
}
// 没有缓存,执行加载配置流程
try {
// 加载整个应用程序中的所有spring.factories文件,
// 将每一个文件对应的url放入Enumeration<URL> urls中,因为各
// 个jar中都可能有spring.factories文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 循环遍历所有的spring.factories文件
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 利用Properties类加载每一个spring.factories文件中的所有属性
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 遍历所有的属性,取出每一个entry中的key,value (工厂类集合名)
for (Map.Entry<?, ?> entry : properties.entrySet()) {
// 注意这里的commaDelimitedListToStringArray方法,将原value按照逗号切割成了一个工厂类名的String集合
List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
// 按照key,List<String> factoryClassNames 存入result这个map中
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
- 接下来看 createSpringFactoriesInstances方法,根据反射创建各value中保存的所有的工厂类名对应的实例
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;
}
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
应用程序监听器对象的初始化方法与以上的一致,只是传入的key为ApplicationListener.class,就不做细致分析了
- 看下推断主应用Class类型的方法
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;
}
- 这里给出一个springboot项目源码的META-INF/spring.factories文件内容
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextnitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
文件中等号前面的就是一个个key,等号后面以逗号分隔的就是工厂类名集合
SpringApplication的构造器就分析到这里,接下来的文章会分析run方法。看看它是如何启动应用程序的
总结
Spring项目利用META-INF下的spring.factories文件做了初始化应用程序上下文类和监听器的定义。这块的知识点还是很重要的,后续实现自定义起步依赖会用到