1. 启动 - 构造方法
先来看看,在SpringBoot中做了什么,来启动我们的应用:
一般我们如此启动应用:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//normal starter
SpringApplication.run(DemoApplication.class, args);
}
在实际工程中,在启动类头上我们会加上需要的组件,例如:
- @EnableFeign
...
等等,这些涉及到自动注入功能,后面再说。
上面的代码实际上到这一步:
//SpringApplication
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
run方法的注释如下:
Run the Spring application, creating and refreshing a new
{@link ApplicationContext}.
@param args the application arguments (usually passed from a Java main method)
@return a running {@link ApplicationContext}
因此,这里我们先来看看,在构造方法中我们做了什么。
1.1 构造方法 - 启航
构造方法中,我们最终到达这一步:
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
//
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();
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这里我们传下来的ResourceLoader是个null,primarySources是我们的启动类。
- 前面几行没啥东西,就是看到的那个样子;
WebApplicationType.deduceFromClasspath()返回的一般都是SERVLET,这个就看我们的相关配置了。
接下来看这个方法:
getBootstrapRegistryInitializersFromSpringFactories
1.1.1 - getBootstrapRegistryInitializersFromSpringFactories
这个方法全文如下:
private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
ArrayList<BootstrapRegistryInitializer> initializers = new ArrayList<>();
getSpringFactoriesInstances(Bootstrapper.class).stream()
.map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize))
.forEach(initializers::add);
initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
return initializers;
}
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
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
这里我们会遇到第一个Spring中很重要的术语:factory:
getSpringFactoriesInstances(Bootstrapper.class)
不过在看这个之前,我们先看看在构造方法中,我们是如何获取这些实例的。
1.1.1.1 getSpringFactoriesInstances
核心的方法为代码块中,获取factoryNames名字和创建工厂实例这两个。
1.我们先看获取名字的
SpringFactoriesLoader.loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
//
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//只有这里是关键的知识
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
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();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
这里的FACTORIES_RESOURCE_LOCATION,实际上是SpringFramework下的META-INF/spring.factories;也就是说:
- 初始化的时候,我们会从这里读取配置文件中指定的。
随后会做一系列的缓存归类等操作,这里我们就先按下不表。
2.获取到名字之后,接下来我们创建工厂类。
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;
}
什么嘛,这里就是用反射来构造对象,没有什么玄乎的东西。
3 回到getSpringFactoriesInstances
这里就没什么可以说的了,现在我们可以对这个方法下个定义:
- 读取配置文件中的factories,这里可能是缓存里读,也可能是从文件里读。
- 如果拿到了,就进行装配。
回到主流程
获取完我们需要的数据之后,就继续进入主流程了。
我们将设置的都放到initializers中:
.forEach(initializers::add);
initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
很遗憾的地方是,在这一步我们并没有获取实际的到instance。如果这里出现了值,可能是版本不一致,也有可能是对配置文件做出了修改。
1.1.2 回到主流程
接下来我们剩三行代码了:
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
上面两行,这次不同的是获取到值了,这里就是跟启动流程中需要的,因此就有数据了。
而最后一行的目的和实现也很简单,就是从当前运行栈中获取启动类的类名。
1.1.3 小结
目前我们分析的是:启动Spring中,
SpringApplication
对象的构造方法。
这里我们从配置文件META-INF/spring.factories中读取了
- ApplicationContextInitializer,设置成Initializer初始化器
- ApplicationListener,设置成监听器
并根据参数以及其他的东西,配置了启动类和类名相关的关键参数。
单凭一个构造方法,可能不太能够构建起对于整个Spring的印象,不过在这里有几个key-point,对于源码脉络有一定的了解:
-
目前我们清楚了,Spring中也内置了spring.factories配置文件,作为初始化时候的一部分配置的配置源。
-
在这里我们根据名称:
- factory
- listener
已经了解到,在构造方法中,Spring已涉及到两个设计模式:
-
工厂方法
-
监听者方法