SpringBoot的启动简述

一、注解和启动类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) })

 

在以上的注解中,我们要关注的有三个注解:

1.1 @SpringBootConfiguration:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

 

@Configuration注解

配置注解的功能是将当前类标注为配置类,并将当前类里以 @Bean 注解标记的方法的实例
注入到spring容器中,实例名即为方法名。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

看到这个 @Component 注解, 意味也将会注册为bean, 其内部也可以依赖注入。@Configuration类通常使用
AnnotationConfigApplicationContext或其支持Web的变体AnnotationConfigWebApplicationContext进行引导。
后续可以继续深入学习spring更底层的运行机制......

1.2 @EnableAutoConfiguration

@EnableAutoConfiguration 注解启用自动配置,其可以帮助SpringBoot应用将所有符合
条件的 @Configuration 配置都加载到当前 IoC 容器之中。@EnableAutoConfiguration 借助 AutoConfigurationImportSelector
的帮助,而后者通过实现 electImports()方法来导出Configuration。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

源码分析:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

AutoConfigurationImportSelector 类的 selectImports() 方法里面通过调用Spring Core 包里 SpringFactoriesLoader

类的 loadFactoryNames()方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}

从 ClassPath下扫描所有的 META-INF/spring.factories 配置文件,并将spring.factories 文件中的 EnableAutoConfiguration
对应的配置项通过反射机制实例化为对应标注了 @Configuration 的形式的IoC容器配置类,然后注入IoC容器。

1.3 @ComponentScan

组件扫描,可自动发现和装配Bean,功能其实就是自动扫描并加载符合条件的组件或者bean定义,最终将这些bean定义加
载到IoC容器中。可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认
Spring框架实现会从声明@ComponentScan所在类的package进行扫描。默认扫描SpringApplication的run方法里的
Booter.class所在的包路径下文件,所以最好将该启动类放到根包路径下。
对于该注解,还可以通过 basePackages 属性来更细粒度的控制该注解的自动扫描范围,比如:

@ComponentScan(basePackages = {"cn.shu.controller","cn.shu.entity"})

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

二、SpringApplication类

我们也需要先构造 SpringApplication 类对象,然后调用该对象的 run() 方法。那么接下来就讲讲 SpringApplication 的构造
过程 以及其 run() 方法的流程,搞清楚了这个,那么也就搞清楚了SpringBoot应用是如何运行起来的!

2.1 实例一个SpringApplication对象,根据类名构造参数。

看一下源码:

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用的类型:创建的是 REACTIVE应用、SERVLET应用、NONE 三种中的某一种
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获得面方法的类,作为启动类。
this.mainApplicationClass = deduceMainApplicationClass();
}

2.2 怎么区别应用类别和上下文:REACTIVE、SERVLET、NONE

SERVLET:{ "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
REACTIVE:    private static final String WEBFLUX_INDICATOR_CLASS = "org."
+ "springframework.web.reactive.DispatcherHandler";
NONE:其他。

static WebApplicationType deduceFromApplicationContext(
Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}

 

2.3  获得所有ApplicationContextInitializer.class的实例,放入初始化容器中。

setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));

UampServletInitializer extends SpringBootServletInitializer implements WebApplicationInitializer

public class ServletContextApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableWebApplicationContext>, Ordered 

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

 

2.4 使用 SpringFactoriesLoader查找并加载 classpath下文件中的所有可用的 ApplicationListener。

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

三、简要分析SpringApplication类核心方法run()

 

3.1 源码如下:

public ConfigurableApplicationContext run(String... args) {
//spring-framework提供了一个StopWatch类可以做类似任务执行时间控制,也就是封装了一个对开始时间,结束时间记录操作的Java类
StopWatch stopWatch = new StopWatch();
stopWatch.start();//计时任务的开始
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//如果为空,就设置ture. System.setProperty("spring.beaninfo.ignore", ignore.toString());
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

3.2 流程图:

SpringBoot的启动简述

 

 

3.3 整个流程简要概括如下:

  • 通过this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)
  • 获取并创建 SpringApplicationRunListener 对象.
  • 然后由 SpringApplicationRunListener 来发出 starting 消息
  • 创建参数,并配置当前 SpringBoot 应用将要使用的 Environment
  • 完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息
  • 初始化 ApplicationContext,并设置 Environment,加载相关配置等
  • 由 SpringApplicationRunListener 来发出 contextPrepared 消息,告知SpringBoot 应用使用的 ApplicationContext 已准备OK
  • 将各种 beans 装载入 ApplicationContext,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用使用的 ApplicationContext 已装填OK
  • refresh ApplicationContext,完成IoC容器可用的最后一步
  • 由 SpringApplicationRunListener 来发出 started 消息
  • 完成最终的程序的启动
  • 由 SpringApplicationRunListener 来发出 running 消息,告知程序已运行起来了
上一篇:通过premake生成vs工程文件


下一篇:springboot启动代码(自用)