• 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);
        }
    
    }
     
     
     


  • 相关阅读:
    C#网络编程(异步传输字符串)
    C#网络编程(同步传输字符串)
    C#网络编程(基本概念和操作)
    Asp.Net 构架(HttpModule 介绍)
    Asp.Net 构架(Http Handler 介绍)
    Asp.Net构架(Http请求处理流程)
    XML的应用 ---- 从一个范例看xml数据、xsd验证、xslt样式
    jQuery的ajax跨域实现
    常见26个jquery使用技巧详解
    常用Request对象获取请求信息
  • 原文地址:https://www.cnblogs.com/Lambquan/p/12024494.html
Copyright © 2020-2023  润新知