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();
// 找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化注册所有实现应用上下文初始化类,Application Context Initializers 
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化注册所有实现ApplicationListener的类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获得当前执行main方法的类对象,作为启动类。
this.mainApplicationClass = deduceMainApplicationClass();
}
ApplicationContextInitializer是Spring框架中的接口,其作用可以理解为在ApplicationContext执行refresh之前,调用ApplicationContextInitializer的initialize()方法,
对ApplicationContext做进一步的设置和处理。spring-boot-xxx.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer。
ApplicationListener是Spring框架中的接口,就是事件监听器,其作用可以理解为在SpringApplicationRunListener发布通知事件时,由ApplicationListener负责接收。
SpringBoot只提供了一个SpringApplicationRunListener的实现类,就是EventPublishingRunListener,起作用就是在SpringBoot启动过程中,负责注册ApplicationListener
监听器,在不同的时点发布不同的事件类型,如果有哪些ApplicationListener的实现类监听了这些事件,则可以接收并处理。
看看spring.factories的配置:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=
org.springframework.boot.env.PropertiesPropertySourceLoader,
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners,仅仅一个实现类
org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=
org.springframework.boot.diagnostics.FailureAnalyzers

# 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.web.context.ServerPortInfoApplicationContextInitializer

# 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.context.logging.ClasspathLoggingApplicationListener,
org.springframework.boot.context.logging.LoggingApplicationListener,
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer

# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

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();
//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
//找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
//之后逐个调用其started()方法,广播SpringBoot要开始执行了。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//如果为空,就设置ture. System.setProperty("spring.beaninfo.ignore", ignore.toString());
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
//根据webEnvironment的值来决定创建何种类型的ApplicationContext对象
//如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
//否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);
//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//初始化所有自动配置类,调用ApplicationContext的refresh()方法
refreshContext(context);

afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
//如果开启日志,则答应执行是时间
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//开始对上下文监听
listeners.started(context);
//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
//该过程可以理解为是SpringBoot完成ApplicationContext初始化前的最后一步工作,
//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//调用异常分析器打印报告,调用所有的SpringApplicationRunListener的finished()方法将异常信息发布出去
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 流程图:

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 消息,告知程序已运行起来了

说明

  • SpringBoot的启动过程,实际上就是对ApplicationContext的初始化过程。
  • ApplicationContext创建后立刻为其设置Environmen,并由ApplicationContextInitializer对其进一步封装。
  • 通过SpringApplicationRunListener在ApplicationContext初始化过程中各个时点发布各种广播事件,并由ApplicationListener负责接收广播事件。
  • 初始化过程中完成IoC的注入,包括通过@EnableAutoConfiguration导入的各种自动配置类。
  • 初始化完成前调用ApplicationRunner和CommandLineRunner的实现类。

3.4 如果要对SpringApplication进行扩展,我们可以选择如下三种方案:

  1. 创建ApplicationContextInitializer的实现类
  2. 创建ApplicationListener的实现类
  3. 创建ApplicationRunner和CommandLineRunner的实现类

四、SpringBootServletInitializer

4.1继承SpringBootServletInitializer

SpringBootServletInitializer用于替代传统mvc模式中的web.xml。如果你要使用外部的sevvlet容器,例如tomcat。
就需要继承该类并重写configure方法。在创建springboot项目时如果你创建的是war,则系统会默认提供一个继承类。
如果创建时选择的是jar,系统不会提供这个继承!需要自己写这个继承。
如果不继承该类会怎么样呢?答:项目无法使用外部容器启动

如果重复继承会怎么样呢?答:项目可以启动,但是会遇到很多不可预期的问题

SpringBootServletInitializer就是一个org.springframework.web.context.WebApplicationContext,容器启动时会调用其onStartup(ServletContext servletContext)方法。
SpringBootServletInitializer的核心方法onStartup,里面最重要的方法是createRootApplicationContext(servletContext)。
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
        ......
    public void onStartup(ServletContext servletContext) throws ServletException {
        this.logger = LogFactory.getLog(this.getClass());
        WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
        if (rootAppContext != null) {
            servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                public void contextInitialized(ServletContextEvent event) {
                }
            });
        } else {
            this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
        }

    }

4.2createRootApplicationContext方法的源码

SpringBootServletInitializer的执行过程,简单来说就是通过SpringApplicationBuilder构建并封装SpringApplication对象,
并最终调用SpringApplication的run方法的过程。

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
        //创建SpringApplicationBuilder,并用其生产出SpringApplication对象
        SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
        builder.main(this.getClass());
        ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
        if (parent != null) {
            this.logger.info("Root context already created (using as parent).");
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
            builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
        }
                //初始化并封装SpringApplicationBuilder对象,为SpringApplication对象增加ApplicationContextInitializer和ApplicationListener做准备
        builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
        //指定创建的ApplicationContext类型
        builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
        //传递入口类,并构建SpringApplication对象
        //可以通过configure()方法对SpringBootServletInitializer进行扩展
        builder = this.configure(builder);
        builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
        SpringApplication application = builder.build();
        if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
            application.addPrimarySources(Collections.singleton(this.getClass()));
        }

        Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
        if (this.registerErrorPageFilter) {
            application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
        }
        //最后调用SpringApplication的run方法
        return this.run(application);
    }

 4.3扩展SpringBootServletInitializer

与扩展SpringApplication类似,ApplicationContextInitializer和ApplicationListener可以基于SpringApplicationBuilder提供
的public方法进行扩展。

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        application.initializers(xxApplicationContextInitializer1,xxApplicationContextInitializer2);
        application.listeners(xxApplicationListener1,xxApplicationListener2)
        return application.sources(xxxApplication.class);
    }

}
 
 
 


原文地址:https://www.cnblogs.com/Lambquan/p/12024494.html