一、项目准备
项目只是一个普通的Springboot项目,引入了web依赖,pox.xml如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zhx</groupId>
<artifactId>sb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sb</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
非常简单,只是几个最基础的依赖。项目中也没有任何配置文件,主类是这样的:
@SpringBootApplication
public class SbApplication {
public static void main(String[] args) {
SpringApplication.run(SbApplication.class, args);
}
}
二、源码解析:创建SpringApplication对象
springboot项目中,都会有一个主类,其上标注有@SpringBootApplication,该主类中有一个main方法,运行该方法就会启动该项目,那么main方法中的SpringApplication.run(SbApplication.class, args);
到底做了什么事?首先,它会创建SpringApplication对象,如下:
1、2、3、4中的代码片段都不必解释,只是简单的代码调用,主要看5的代码片段,该代码片段一共可以分解成五部分:
First:this.sources.addAll(Arrays.asList(sources));
该initialize方法,接受一个Object数组,由上下文可知,该数组中只有一个元素:SbApplication.class,接着将该元素装入到SpringApplication对象中的局部变量private final Set<Object> sources = new LinkedHashSet<Object>();
中。
Second:this.webEnvironment = deduceWebEnvironment();
//该实例变量用于记录该项目是否是一个web项目
private boolean webEnvironment;
//第一个接口是jdk中定义的接口,第二个接口的定义在org.springframework:spring-web这个项目中
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
//deduce这个单词的意思是推断,该方法主要是为了推断出当前项目是否是一个web环境
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
//加载className,如果该className不存在或者不能被类加载器加载,返回false;如果存在并且可以被加载,返回true
public static boolean isPresent(String className, ClassLoader classLoader) {
try {
//该方法在jdk本身提供的Class.forName方法的基础上,还可以加载primitives、arrayClassClass、innerClass
forName(className, classLoader);
return true;
} catch (Throwable var3) {
return false;
}
}
Third:setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
代码片段7不必解释,仅仅是调用了代码片段8:
private <T> Collection<? extends 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<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//实例话上一步的这些类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//对instances按照order排序,但是发现有的类没有实现org.springframework.core.Ordered接口,如果必要,以后可以研究
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
可以看出,该方法会根据传入的type类型,从spring.factories文件中获取对应的很多全类名:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
//获取factoryClass的全类名,此处为org.springframework.context.ApplicationContextInitializer
String factoryClassName = factoryClass.getName();
try {
//加载META-INF/spring.factories这个文件到urls中,该urls中一共有两个元素:
//spring-boot项目下的:org/springframework/boot/spring-boot/1.5.8.RELEASE/spring-boot-1.5.8.RELEASE.jar!/META-INF/spring.factories
//该文件下以org.springframework.context.ApplicationContextInitializer为键的值一共有四个,见图11
//spring-boot-autoconfigure项目下的org/springframework/boot/spring-boot-autoconfigure/1.5.8.RELEASE/spring-boot-autoconfigure-1.5.8.RELEASE.jar!/META-INF/spring.factories
//该文件下以org.springframework.context.ApplicationContextInitializer为键的值一共有两个,见图12
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
接着借助Spring的工具类BeanUtils,通过构造函数反射实例话它们并返回:
//创造上面从spring.factory中读取的org.springframework.context.ApplicationContextInitializer的实例
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(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;
}
最后将实例化好的这些类放到SpringApplication的private List<ApplicationContextInitializer<?>> initializers;
属性中:
private List<ApplicationContextInitializer<?>> initializers;
//将上面创建好的并且排好序的实例设置到SpringApplication的initializers属性中
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers.addAll(initializers);
}
Fourth:setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
这里的方法调用和上一步一样,唯一不同的是加载的是spring.factories下的org.springframework.context.ApplicationListener,然后保存到SpringApplication对象的private List<ApplicationListener<?>> listeners;
中。
spring.factories文件的位置主要在三个地方:spring-boot-1.5.8.RELEASE.jar(图13)、spring-boot-autoconfigure-1.5.8.RELEASE.jar(图14):
Fifth:this.mainApplicationClass = deduceMainApplicationClass();
这个方法是根据调用脸推测出main 方法所在的类,并返回它的Class对象,赋值给SpringApplication的private Class<?> mainApplicationClass;
变量:
private Class<?> deduceMainApplicationClass() {
try {
//调用链
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
//如果链路上的方法名包含了main,则加载这个类并返回
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
至此,关于创建SpringApplication对象的源码就已经解读完了,需要记得的是,我们在该对象的几个变量中存放了一些解析出来的信息,这至关重要,因为以后可能会用到它们。
三、总结
创建SpringApplication对象,其实就是初始化该对象的几个变量:
- private final Set sources = new LinkedHashSet();
值为项目主类的Class对象:SbApplication.class - private boolean webEnvironment;
值为true,表示该项目是一个web项目 - private List<ApplicationContextInitializer<?>> initializers;
值为:图11和图12 - private List<ApplicationListener<?>> listeners;
值为:图13和图14 - private Class<?> mainApplicationClass;
值为项目主类的Class对象:SbApplication.class
四、抛出问题
经过上面的解析,看似我们对创建SpringApplication对象已经比较熟悉了,其实则不然,我们花了那么多篇幅去解析initializers和listeners是如何被初始化赋值的,那么请问:
第一:什么是ApplicationContextInitializer
第二:什么是ApplicationListener
且看下次分解。。。。。。