在上一篇,咱们主要介绍了,注解@SpringbootApplication的自动化配置原理,那么首先咱们先回顾一下,这个注解主要为咱们的sprngboot工程作了什么: 咱们能够将自动配置的关键几步以及相应的注解总结以下:java
1.@Configuration&与@Bean------>>>基于java代码的bean配置2.@Conditional-------->>>>>>设置自动配置条件依赖react
3.@EnableConfigurationProperties与@ConfigurationProperties->读取配置文件转换为bean。web
4.@EnableAutoConfiguration、@AutoConfigurationPackage与@Import->实现bean发现与加载。 spring
今天,咱们来经过源码来分析一下它的启动过程.编程
本篇基于 2.0.4.RELEASE 版本进行分析,阅读本文须要有一些 Java 和 Spring 框架基础,若是还不知道 Spring Boot 是什么,建议先看下官网的 Spring Boot 教程。springboot
上面是 Spring Boot 最简单通用的入口类。入口类的要求是最顶层包下面第一个含有 main 方法的类,使用注解 @SpringBootApplication 来启用 Spring Boot 特性,使用 SpringApplication.run 方法来启动 Spring Boot 项目。app
首先来看一下这个类里面run方法的调用源码:框架
第二个参数 args:传递给应用的应用参数。spring-boot
先用主要资源类来实例化一个 SpringApplication 对象,再调用这个对象的 run 方法,因此咱们分两步来分析这个启动源码。源码分析
跟着上面的run进入到下面的方法:
进入SpringApplication能够看到如下源码:
从上面的源码能够知道,整个实例化过程有7个步骤:
this.resourceLoader = resourceLoader;
复制代码
Assert.notNull(primarySources, "PrimarySources must not be null");
复制代码
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
复制代码
this.webApplicationType = WebApplicationType.deduceFromClasspath();
复制代码
这里进去WebApplicationType方法看一下源码以及相关构造方法:
public enum 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;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false;
}
}
}
复制代码
这个就是根据类路径下是否有对应项目类型的类推断出不一样的应用类型,这里也说明 Spring Boot 2是支持响应式编程。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
复制代码
进入ApplicationContextInitializer,咱们能够知道其做用:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/** * Initialize the given application context. * @param applicationContext the application to configure */
void initialize(C applicationContext);
}
复制代码
用来初始化指定的 Spring 应用上下文,如注册属性资源、激活 Profiles 等。
再来看下 setInitializers 方法源码,其实就是初始化一个 ApplicationContextInitializer 应用上下文初始化器实例的集合。
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
复制代码
最后咱们来看一下核心方法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;
}
复制代码
设置应用上下文初始化器可分为如下 5 个步骤。 这里是实例化的核心:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
复制代码
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
复制代码
loadFactoryNames 的源码以下:
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
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 factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
复制代码
根据类路径下的 META-INF/spring.factories 文件解析并获取 ApplicationContextInitializer 接口的全部配置的类路径名称。
spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories 的初始化器相关配置内容以下:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
# Auto Configure
......
复制代码
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
复制代码
AnnotationAwareOrderComparator.sort(instances);
复制代码
return instances;
复制代码
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
复制代码
ApplicationListener 的做用是什么?源码以下。(有空再写一篇springboot2 监听器的应用)
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/** * Handle an application event. * @param event the event to respond to */
void onApplicationEvent(E event);
}
复制代码
看源码,这个接口继承了 JDK 的 java.util.EventListener 接口,实现了观察者模式,它通常用来定义感兴趣的事件类型,事件类型限定于 ApplicationEvent 的子类,这一样继承了 JDK 的 java.util.EventObject 接口。
设置监听器和设置初始化器调用的方法是同样的,只是传入的类型不同,设置监听器的接口类型为:getSpringFactoriesInstances,对应的 spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories 文件配置内容在上面的配置文件里面: Application Listeners
经过文件能够看出当前只有一个 BackgroundPreinitializer 监听器。
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;
}
复制代码
经过构造一个运行时异常,再遍历异常栈中的方法名,获取方法名为 main 的栈帧,历来获得入口类的名字再返回该类。
今天主要分析SpringBoot初始化实例的源码分析,本章暂时分析到: