内容
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SpringbootWebApp {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebApp.class, args);
}
}
这是一个常见的springboot启动程序,首先进入run方法:
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
方法的第一个参数primarySource就是我们传入的启动类的Class对象,第二个可变参数就是main方法的命令参数
进入第二个重载方法run
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
再进入SpringApplication的构造方法,传入了一个参数就是我们的启动类的类对象
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #SpringApplication(ResourceLoader, Class...)
* @see #setSources(Set)
*/
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();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这个方法的第一个参数是空,第二个就是启动类对象,这个方法体做了以下事情:
1、设置SpringApplication对象的resourceLoader,这里设置的是空
2、把启动类对象包装成集合赋值给primarySources属性
3、推断应用类型,这里就是servletApplication类型
4、设置初始化器
5、设置监听器
6、设置main方法所在的类
除了五六两条,其他逻辑都很简单,点进去方法一看就知道了。
先看第五条:
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
先进入到方法setInitializers()里面:
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
其实很简单,就是把传进来的参数设置到SpringApplication对象的集合属性里面,那么这些initializers是怎么来的呢?这个才是这里的重点。
进入到方法:
getSpringFactoriesInstances(
ApplicationContextInitializer.class)
这个方法是SpringApplication通用的方法,就是从jar包里的所有spring.factory文件中读取所有的实现了ApplicationContextInitializer接口的类名称,并实例化。具体这个方法逻辑是怎样的呢?直接进入到方法体:
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 = 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;
}
请看第二个方法体,它大概做了这么几件事情:
1、获取类加载器
2、从spring.factory文件里面加载对应类型的类的名字集合
3、实例化这些类
4、排序
接下来进入到SpringFactoriesLoader.loadFactoryNames(type, classLoader)
这个方法体内:
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
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()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
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);
}
}
这两个方法都进入到了类SpringFactoriesLoader里面了
SpringApplication类把这个加载工作全权委托给了SpringFactoriesLoader。
其中这个类有个静态属性就是/** * 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";
通过第二个方法可以看出来,它就是通过类加载器加载类路径下面的名称为spring.factories的文件,由于可能有多个这样的文件,因为还可能依赖很多三方包,所以通过加载后得到的是一个url集合,然后遍历这个集合,把每个url指向的文件加载到内存:Properties properties = PropertiesLoaderUtils.loadProperties(resource);
包装成了一个properties对象,properties大家就很熟悉了它继承map是一个kv的数据结构,然后把这些kv放入到MultiValueMap中返回。
然后根据加载的类的名称作为key,到这个MultiValueMap中获取到对应类的实例的集合再返回给上一级方法。
所以Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader));
这行代码其实就是从spring.factories文件中获取所有实现了某个接口的类名称字符串的集合。
接下来就是实例化这些类,所以分析List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
这行代码。
进入方法体:
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;
}
这个方法就不用解释,太简单了,其实就是循环上一步得到的那些类的名称集合,然后通过反射实例化,放入另外一个集合中返回。
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();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这个方法的setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
这行代码就执行完了,一句话概括就是从所有spring.factories文件中获取实现ApplicationContextInitializer接口的类的名称,然后实例化加入到SpringApplication对象的属性initializers集合中。
至于
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
这行代码和上一行代码的逻辑完全一样,只不过它是把所有的
实现ApplicationListener接口的类实例化后加入到了listeners属性中,至此SpringApplication对象就实例化完成了。
总结
如果我们调用SpringApplication类的静态方法run,它会首先执行自身的实例化,然后再调用实例方法run。
本节主要分析了SpringApplication类的实例化源码。
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();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
总的来说,实例化过程做了一下工作:
1、把参数传入的资源加载器设置到属性中
2、设置主源类信息,作为后续加载beandefination的入口
3、推断本次程序的容器类型
4、设置初始化器
5、设置监听器
6、推断main方法所在的类