首页 > 其他分享 >SpringBoot启动过程(一)

SpringBoot启动过程(一)

时间:2022-10-23 22:31:14浏览次数:68  
标签:return SpringBoot 启动 spring classLoader order 实例 过程 Class


1,创建SpringApplication实例

1.1 设置WebApplicationType(应用类型)

目前有三种类型,SERVLET或者REACTIVE或者NONE

判断是根据某些特定的类是否存在来判断的,

具体推断方法为deduceFromClasspath,代码

如下

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;
}

1.2 设置初始化器initializers

设置初始化器主要分2步,第一步,找到需要初始化的 初始化器的名字

第二步,根据上一步找到的名字,创建实例。


1.2.1 找名字

所有初始化器都是ApplicationContextInitializer这个接口的实现

ApplicationContextInitializer代码如下

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);

}


这里主要用到了getSpringFactoriesInstances获取工厂实例的方法


这里遇到了静态方法 SpringFactoriesLoader.loadFactoryNames

SpringFactoriesLoader这个类属于spring-core

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

loadSpringFactories这个方法,从类加载器中load所有的Factories,形成一个Map,这里只取执行类型的加载器返回,如这一步,取的是ApplicationContextInitializer的实现,下一步取的是ApplicationListener的


现在看一下 loadSpringFactories 这个方法,如下

  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

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


这里用到类加载器的方法 classLoader.getResources(), 功能是根据所给的名字,找到所有的资源,这个方法是JDK实现的。找到的资源如下

jar:file:/Users/fupeng/.m2/repository/org/springframework/data/spring-data-jdbc/1.0.8.RELEASE/spring-data-jdbc-1.0.8.RELEASE.jar!/META-INF/spring.factories

然后利用PropertiesLoaderUtils.loadProperties(resource)方法,读取该文件的内容,这个方法是spring-core实现的


下图是某一次启动在所有spring.factories中加载到的类名,可以看到最多的是 EnableAutoConfiguration,下面有288个之多。很多自定义的都放到这个下面去了。

SpringBoot启动过程(一)_spring

在启动这里,这些都不需要,只取实现了ApplicationContextInitializer的类名,找到名字后,下面就可以 创建实例了。


1.2.2 创建实例

先看一下代码的位置,现在开始根据名字 实例化类了。

    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;
}

根据类名,创建实例,代码如下

  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;
}

主要代码是,根据完整的类名字,生成Class对象,

在利用Class对象,实例化。

spring beans 会优先创建一个Kotlin的类,如果这个不存在时,才会用Class的newInstance方法创建实例

至此,实例都创建出来了,下一步是排序。


1.2.3 对实例进行排序

具体方法就一行代码,如下

AnnotationAwareOrderComparator.sort(instances);


排序的关键是确定实例的order。有多个地方可以设定order,它们是有优先级的。确定order的类是AnnotationAwareOrderComparator

最高优先级是 实现了Ordered接口

public interface Ordered {

/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;


/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
int getOrder();

}

如果没有实现Ordered接口,其次是在类上放置了 Order 注解,会去这个注解的值。这个注解是spring-core自己的


如果没有Order注解,第三档是 javax.annotation.Priority 这个注解的值,这个注解是JDK的。


如果这个注解也没有,那么就是默认的order,也就是最低优先级。

int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

order是数字越大,优先级越低。

一般情况,order=0 就是优先级的第一梯队了。


本文完


但是SpringApplication还没有实例化完成,下一篇继续。

标签:return,SpringBoot,启动,spring,classLoader,order,实例,过程,Class
From: https://blog.51cto.com/u_15815563/5787667

相关文章