SpringBoot 启动解析
入口
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JamsAuthApplication {
public static void main(String[] args) {
SpringApplication.run(JamsAuthApplication.class, args);
}
}
主要注解
@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//...
}
可以看到,@SpringBootApplication是一个组合注解,主要包含@SpringBootConfiguration、@EnableAutoConfiguration,@ComponentScan等几个注解,并没有什么特别的地方。
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@SpringBootConfiguration实际上是@Configuration。
@Configuration
基于JavaConfig形式的Spring Ioc容器的配置类。
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类;
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
@ComponentScan
@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
相当于配置中的:
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描,所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class>[] exclude() default {};
String[] excludeName() default {};
}
这个注解是SpringBoot自动配置的关键,主要是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,后面有机会在深入。
入口方法
SpringApplication.run(JamsAuthApplication.class, args);
进入run方法后,
public static ConfigurableApplicationContext run(Class> primarySource, String... args) {
return run(new Class>[] { primarySource }, args);
}
可以看到,调用重载的方法
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}
上面的代码会创建一个SpringApplication实例,并调用其run方法,先看构造方法。
1.SpringApplication构造方法
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));
// 判断是否是web程序(javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext都必须在类加载器中存在),并设置到webEnvironment属性中
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从spring.factories文件中找出key为ApplicationContextInitializer的类并实例化后设置到SpringApplication的initializers属性中。这个过程也就是找出所有的应用程序初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从spring.factories文件中找出key为ApplicationListener的类并实例化后设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 找出main类
this.mainApplicationClass = deduceMainApplicationClass();
}
deduceFromClasspath():推断类型
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
setInitializers():设置程序初始化器
从spring.factories文件中找出key为ApplicationContextInitializer的类并实例化后设置到SpringApplication的initializers属性中。这个过程也就是找出所有的应用程序初始化器.
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
private Collection getSpringFactoriesInstances(Class type) {
return getSpringFactoriesInstances(type, new Class>[] {});
}
private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 使用Set保存names来避免重复元素,加载在 META-INF/spring.factories 里的类名的数组
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据names来进行实例化
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//// 对实例进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
设置初始值
public void setInitializers(Collection extends ApplicationContextInitializer>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
setListeners():设置监听器
从spring.factories文件中找出key为ApplicationListener的类并实例化后设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器,
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
与初始化initializers相似 。
deduceMainApplicationClass():推断应用入口类
this.mainApplicationClass = deduceMainApplicationClass();
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;
}
它通过构造一个运行时异常,通过异常栈中方法名为main的栈帧来得到入口类的名字。
2.SpringApplication.run()方法
public ConfigurableApplicationContext run(String... args) {
// 计时工具,StopWatch 主要用于简单统计 run 启动过程的时长
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
// 设置java.awt.headless系统属性为true - 没有图形化界面
configureHeadlessProperty();
// 1.获得SpringApplicationRunListener 的数组,并启动监听
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2.加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//准备Banner打印
Banner printedBanner = printBanner(environment);
//3.创建Spring容器
context = createApplicationContext();
// 准备异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//4.Spring容器前置处理
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新Spring 容器
refreshContext(context);
//Spring容器后置处理
afterRefresh(context, applicationArguments);
//停止计时器
stopWatch.stop();
if (this.logStartupInfo) {
//打印 Spring Boot 启动的时长日志
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 通知 SpringApplicationRunListener 的数组,Spring 容器启动完成
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//处理异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//通知 SpringApplicationRunListener 的数组,Spring 容器正在运行
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
1.getRunListeners():所谓获得数组,并启动监听。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class>[] types = new Class>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
可以看到这里也是调用了getSpringFactoriesInstances()方法来获取org.springframework.boot.SpringApplicationRunListener的values.作用后面遇到再细看。
2.prepareEnvironment():根据SpringApplicationRunListeners以及参数来准备环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根据 webApplicationType 类型,会创建不同类型的 ConfigurableEnvironment 对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 通知监听器环境变量准备完成
listeners.environmentPrepared(environment);
// 绑定
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
configureEnvironment():配置环境:Property Sources和Profiles
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
3.createApplicationContext():创建spring上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
4.prepareContext():容器前置处理
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 关联环境与容器
context.setEnvironment(environment);
// 为容器配置Bean生成器以及资源加载器
postProcessApplicationContext(context);
// 初始化
applyInitializers(context);
// 通知监听器容器准备完成
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
//添加两个特殊的bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 加载所有资源
Set
可以看到这里applyInitializers(context)用到了实例化设置的初始化器了
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
总结
以上大概走了springboot的启动流程,中间还有一些方法尚不清楚,待后续补充...