如下代码所示:
//main方法A
public static void main_A(String[] args) {
SpringApplication springApplication = new SpringApplication(TestSpring.class);
springApplication.run(args);
}
//main方法B
public static void main_B(String[] args) {
SpringApplication.run(TestSpring.class,args);
}
对于这两个代码段,是我们进行springboot开发时常用的两种启动类写法,这里我们通过观察springboot的SpringApplication这个类的代码可以发现mainB方法其实调用的是这些代码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);//这个run调用的是下面的run
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
也就是说mainB这种写法其实是完全等价于mainA这种写法的。而对于mainA这种方法去调用springApplication对象的成员方法run方法中的这个run方法,我们一会进行解读,先观察SpringApplication这个类是如何创建对象的(之所以要研究构造方法,是因为mainB这种写法其实底层还是创建了一个springApplication对象,所以我们要研究这个对象是如何创建出来的)
1 public SpringApplication(Class<?>... primarySources) {
2 this(null, primarySources);
3 }
4 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
5 this.resourceLoader = resourceLoader;
6 Assert.notNull(primarySources, "PrimarySources must not be null");
7 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
8 this.webApplicationType = WebApplicationType.deduceFromClasspath();
9 this.bootstrapRegistryInitializers = new ArrayList<>(
10 getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
11 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
12 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
13 this.mainApplicationClass = deduceMainApplicationClass();
14 }
无论是mainA还是mainB,最后调用SpringApplication的构造方法都是调用的上面代码的第一行这个构造方法,而第一行这个构造方法又调用了第4行这个构造方法,所以我们研究一下这个第四行的构造方法干了什么事情。
5 this.resourceLoader = resourceLoader;
6 Assert.notNull(primarySources, "PrimarySources must not be null");
7 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
8 this.webApplicationType = WebApplicationType.deduceFromClasspath();
9 this.bootstrapRegistryInitializers = new ArrayList<>(
10 getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
11 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
12 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
13 this.mainApplicationClass = deduceMainApplicationClass();
我单独把上面5-13行的代码复制出来,方便我们分析,首先第5行是对springApplication的一个资源加载器进行赋值,但是我们无论是通过mainA还是mainB去启动一个springboot项目,这个资源加载器我们都是没有赋值的,也就是resourceLoader属性应该是null。第6行是一个断言,为了第7行赋值,第7行通过分析,可以知道这个位置其实就是赋值一下我们自己写的主启动类(分析不明白的,请自行研究源码,一步一步跟踪,很简单的),也就是TestSpring这个类。第8行,等号右边的这个WebApplicationType.deduceFromClasspath()这个方法,其实是一个枚举内部的方法,WebApplicationType就是一个枚举类,而这个第8行的作用就是判断当前这个springboot项目是否是一个web项目,而判断的方式很简单就是看我们的项目是否又spring-web的相关依赖(这也解释了为什么,小伙伴们自己去写springboot项目,pom文件里只用springboot依赖不用springweb依赖,就会发现项目启动了之后就立刻停止。这个属性会决定是否开启spring内置的tomcat),第9行第10行第11行和第12行这四行我们一起来观察。其实他们都调用了一个叫做getSpringFactoriesInstances的方法,这个方法做的事情就是去检查META-INF/spring.factories这个文件里面的自动装配信息,并形成一层缓存,这么说,小伙伴们可能不太清楚啥意思,我给大家贴一下代码
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;
}
我们关注一下第二个getSpringFactoriesInstances方法,为什么是第二个呢,因为第一个最后调用的也是第二个,QAQ。第二个方法首先第一步搞一个classLoader,而我们刚才研究构造方法的时候,就有一个对resourceLoader赋值的操作,虽然我们赋值的是null,那这里面有什么联系吗?
public ClassLoader getClassLoader() {
if (this.resourceLoader != null) {
return this.resourceLoader.getClassLoader();
}
return ClassUtils.getDefaultClassLoader();
}
可以发现,classLoader本来打算从resourceLoader中获取,如果resourceLoader中没有的话,去调用ClassUtils.getDefaultClassLoader(),至于这个方法,我们不过多展开去看,反正getClassLoader这个方法就是返回了一个classLoader对象。然后我们继续关注之前的getSpringFactoriesInstance方法(这里就不再粘贴代码了,因为代码前面粘贴完了),首先搞了一个classLoader,紧接着又搞了一个names的一个set集合,里面的数据是通过调用SpringFactoriesLoader.loadFactoryNames()这个方法获取到的,而且将type参数和刚获取的classLoader传入进去,我们明确一下getSpringFactoriesInstance方法有一个参数是type这个参数是一个字节码对象,那也就是说SpringFactoriesLoader.loadFactoryNames()这个方法需要一个字节码对象和一个类加载器,其实根据我们已知的信息,充分发挥主观想象力就可以得知,这个方法就是通过给定的类加载器去加载指定的字节码对象,但是通过返回值是一个泛型为String的set集合我们可以知道,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());
}
首先对classLoaderToUse进行赋值并判空,并且获取factoryType字节码参数的全限定类名,然后调用一个loadSpringFactories方法,该方法返回的是一个map,并要获取这个map里key值为字节码参数的全限定类名的值。那么我们继续研究一下loadSpringFactories这个方法
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;
}
解读一下第一行从cache中获取classLoader对应的value值,如果获取到了就直接返回。所以前4行没啥好说的,相当于搞了一个缓存机制。我们继续往下看,在try-catch代码块中第一行就搞了一个classLoader.getResources(FACTORIES_RESOURCE_LOCATION)这个方法,而参数FACTORIES_RESOURCE_LOCATION的值实际上是"META-INF/spring.factories",那也就是说,要加载这个文件,然后下面的代码其实就是解析这个文件里面的内容,至于这个文件有什么作用,可以自行百度。
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;
}
再回过头来看这个代码,我们搞懂了SpringFactoriesLoader.loadFactoryNames这个方法其实就是加载各种的META-INF/spring.factories这个文件,并解析,而且还做了一层缓存,也就是说getSpringFactoriesInstances这个方法无论被调用多少次,底层找文件,解析,这个过程只会触发一次。然后我们继续看createSpringFactoriesInstances这个方法
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;
}
这个方法其实就是通过反射去实例化对象,然后返回一个list。
我们再看一下这行代码AnnotationAwareOrderComparator.sort(instances);
public class AnnotationAwareOrderComparator extends OrderComparator{
public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
public static void sort(List<?> list) {
if (list.size() > 1) {
list.sort(INSTANCE);
}
}
}
这行api调用AnnotationAwareOrderComparator 这个类的compare方法(为什么是这个方法,java基础告诉我们List的sort方法需要传进来一个比较器,并且在底层调用比较器的compare方法),而这个类的父类OrderComparator实现了比较器这个接口
public class OrderComparator implements Comparator<Object> {
public static final OrderComparator INSTANCE = new OrderComparator();
@Override
public int compare(@Nullable Object o1, @Nullable Object o2) {
return doCompare(o1, o2, null);
}
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
boolean p1 = (o1 instanceof PriorityOrdered);
boolean p2 = (o2 instanceof PriorityOrdered);
if (p1 && !p2) {
return -1;
}
else if (p2 && !p1) {
return 1;
}
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}
private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
Integer order = null;
if (obj != null && sourceProvider != null) {
Object orderSource = sourceProvider.getOrderSource(obj);
if (orderSource != null) {
if (orderSource.getClass().isArray()) {
for (Object source : ObjectUtils.toObjectArray(orderSource)) {
order = findOrder(source);
if (order != null) {
break;
}
}
}
else {
order = findOrder(orderSource);
}
}
}
return (order != null ? order : getOrder(obj));
}
protected int getOrder(@Nullable Object obj) {
if (obj != null) {
Integer order = findOrder(obj);
if (order != null) {
return order;
}
}
return Ordered.LOWEST_PRECEDENCE;
}
@Nullable
protected Integer findOrder(Object obj) {
return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
}
}
有兴趣的同学可以去阅读一下这个类的代码,我把跟比较相关的代码都保留下来了,这个代码还是比较便于理解的,大概意思就是说,o1o2两个对象,首先看看他们谁实现了PriorityOrdered接口,谁实现这个接口并且另一个没实现,则实现接口的对象小,如果俩个对象都没实现这个接口,那么通过其他的比较情况继续去比较,这里只考虑实现compare这个方法,也就是说,如果没人实现PriorityOrdered这个方法,那么就看他俩有没有实现Ordered接口,实现这个接口的话,就调用getOrder方法,获取比较值,否则就返回一个整型最大值。
至此我们解释完了AnnotationAwareOrderComparator.sort(instances);这行代码之后,SpringApplication的getSpringFactoriesInstances这个方法也就介绍完毕。
回到构造方法那里
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
1 this.resourceLoader = resourceLoader;
2 Assert.notNull(primarySources, "PrimarySources must not be null");
3 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
4 this.webApplicationType = WebApplicationType.deduceFromClasspath();
5 this.bootstrapRegistryInitializers = new ArrayList<>(
6 getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
7 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
8 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
9 this.mainApplicationClass = deduceMainApplicationClass();
}
5,6,7,8这四个代码都调用了getSpringFactoriesInstances这个方法,而这个方法就是说,从所有META-INF\spring.factories文件中获取指定字节码对象的内容,并初始化返回。
第9行是给住启动类进行赋值,不过多深入讲解.至此如何new出来一个SpringApplication对象就介绍完毕。
总结一下哈,如果new出来一个SpringApplication对象,首先对SpringAppplication这个对象的一些属性赋值,并判断是否是一个web应用,并且初始化BootstrapRegistryInitilizer、ApplicationContextInitializer、ApplicationListener这三个接口的实现类。