文章目录
我们生成一个spring boot 项目时,会自带一个启动类. 代码如下:
@SpringBootApplication
public class AdminBootstrap {
public static void main(String[] args) {
new SpringApplicationBuilder(AdminBootstrap.class).web(true).run(args);
}
}
就是这么简单的代码,构成了spring boot的世界. 那么代码中只有一个@SpringBootApplication 注解 和 调用了SpringApplication#run方法.那么我们先来解析SpringApplication的run方法.
SpringApplication初始化
- 首先调用
SpringApplication.initialize
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
//1.如果sources长度大于0的话,加入到SpringApplication的sources中,该sources是一个LinkedHashSet.
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//2.调用deduceWebEnvironment方法判断是否是web环境
this.webEnvironment = deduceWebEnvironment();
//3.设置initializers.
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//4.设置Listeners.
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//5.设置mainApplicationClass.
this.mainApplicationClass = deduceMainApplicationClass();
}
- deduceWebEnvironment发现会调用ClassUtils类的isPresent方法,检查classpath中是否存在javax.servlet.Servlet类和org.springframework.web.context.ConfigurableWebApplicationContext类,如果存在的话,返回true.否则返回false.
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
- 在设置Initializers时首先调用getSpringFactoriesInstances方法加载ApplicationContextInitializer.然后直接赋值给initializers.代码如下:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
//1.首先获得ClassLoader
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
//2.调用SpringFactoriesLoader#loadFactoryNames进行加载,然后放入到LinkedHashSet进行去重.
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//3.调用createSpringFactoriesInstances进行初始化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//4.对实例进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
- 使用给定的类加载器从“META-INF / spring.factories”加载给定类型的工厂实现的完全限定类名。
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
//1.获得factoryClassName,对于当前来说factoryClassName =org.springframework.context.ApplicationContextInitializer.
String factoryClassName = factoryClass.getName();
try {
//2.通过传入的classLoader加载META-INF/spring.factories文件.
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
//3.通过调用PropertiesLoaderUtils#loadProperties将其转为Properties.
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
//4.获得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-boot/META-INF/spring.factories中.org.springframework.context.ApplicationContextInitializer值如下:
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
createSpringFactoriesInstances
遍历传入的names,也就是之前通过SpringFactoriesLoader加载的类名.通过遍历,依次调用其构造器进行初始化.加入到instances.然后进行返回.
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;
}
对于当前场景来说:
ConfigurationWarningsApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer初始化没有做任何事.
ContextIdApplicationContextInitializer在初始化时.会获得spring boot的应用名.搜索路径如下:
spring.application.name
vcap.application.name
spring.config.name
如果都没有配置的话,返回application
private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}";
public ContextIdApplicationContextInitializer() {
this(NAME_PATTERN);
}
public ContextIdApplicationContextInitializer(String name) {
this.name = name;
}
- 设置SpringApplication#setListeners时,还是同样的套路.调用getSpringFactoriesInstances加载META-INF/spring.factories中配置的org.springframework.context.ApplicationListener.
对于当前来说.加载的类如下:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener
这些类在构造器中都没有做任何事.
- 调用SpringApplication#deduceMainApplicationClass方法.获得应用的启动类.该方法通过获取当前方法调用栈,找到main函数的类.代码如下:
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
SpringApplication的run方法
public ConfigurableApplicationContext run(String... args) {
//1.初始化StopWatch,调用其start方法开始计时.
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//2.调用configureHeadlessProperty设置系统属性java.awt.headless,这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能
configureHeadlessProperty();
//3.调用SpringApplicationRunListeners#starting
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//4.创建一个DefaultApplicationArguments对象,它持有着args参数,就是main函数传进来的参数.调用prepareEnvironment方法.
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//5.打印banner
Banner printedBanner = printBanner(environment);
//6.创建SpringBoot上下文
context = createApplicationContext();
//7.初始化FailureAnalyzers
analyzers = new FailureAnalyzers(context);
//8.调用prepareContext
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//9.调用AbstractApplicationContext#refresh方法,并注册钩子
refreshContext(context);
//10.在容器完成刷新后,依次调用注册的Runners
afterRefresh(context, applicationArguments);
//11.调用SpringApplicationRunListeners#finished
listeners.finished(context, null);
//12.停止计时
stopWatch.stop();
//13.初始化过程中出现异常时调用handleRunFailure进行处理,然后抛出IllegalStateException异常.
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}