微服务《SpringBoot学习》重点

SpringBoot托管配置:

  • Springboot实际上通过约定大于配置的方式,使用 xx-starter统一的对Bean进行默认初始化,用户只需要很少的配置就可以进行开发了。

SpringBoot做了什么简化Spring应用的创建、运行、调试、部署。约定大于配置。

  • starter快速整合常用的第三方依赖:原理是通过Maven子父工程的方式。
  • 全部采用注解形式:简化XML配置。
  • 内置Http服务器

启动流程

SpringApplication是springboot驱动spring应用上下文的引导类,他的run()方法启动Spring应用,实质上是为Spring应用创建并初始化Spring上下文:

\1. 初始化监听器,以及添加到SpringApplication的自定义监听器。

\2. 发布ApplicationStartedEvent事件,如果想监听ApplicationStartedEvent事件,你可以这样定义:public class ApplicationStartedListener implements ApplicationListener,然后通过SpringApplication.addListener(..)添加进去即可。

\3. 装配参数和环境,确定是web环境还是非web环境。

\4. 装配完环境后,就触发ApplicationEnvironmentPreparedEvent事件。

\5. 如果SpringApplication的showBanner属性被设置为true,则打印启动的Banner。

\6. 创建ApplicationContext,会根据是否是web环境,来决定创建什么类型的ApplicationContext。

\7. 装配Context的环境变量,注册Initializers、beanNameGenerator等。

\8. 发布ApplicationPreparedEvent事件。

\9. 注册springApplicationArguments、springBootBanner,加载资源等

\10. 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

\11. 调用ApplicationContext的refresh()方法,装配context beanfactory等非常重要的核心组件。

\12. 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

\13. 发布ApplicationReadyEvent事件,启动完毕,表示服务已经可以开始正常提供服务了。通常我们这里会监听这个事件来打印一些监控性质的日志,表示应用正常启动了。

SpringBoot会触发其他的一些事件,这些事件按下列顺序触发:

(1)ApplicationStartingEvent:项目刚启动时触发,此时除了注册监听器和初始器之外,其他所有处理都没有开始;

(2)ApplicationEnvironmentPreparedEvent:上下文得到环境信息之后触发,此时上下文创建还没有创建;

(3)ApplicationPreparedEvent:bean的定义信息加载完成之后触发,此时bean还没有初始化;

(4)ApplicationReadyEvent:在所有bean初始化完毕,所有回调处理完成,系统准备处理服务请求时触发;

(5)ApplicationFailedEvent:启动过程出现异常时候触发。

SpringApplication.run()

如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

\1. 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。

\2. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。

\3. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。

\4. 推断并设置main方法的定义类。

它会执行以下步骤:

\1. 创建一个合适的ApplicationContext实例 (取决于classpath)。

\2. 注册一个CommandLinePropertySource,以便将命令行参数作为Spring properties。

\3. 刷新application context,加载所有单例beans。

\4. 激活所有CommandLineRunner beans。

SpringApplication详解(run执行启动)

SpringApplication是springboot驱动spring应用上下文的引导类,他的run()方法启动Spring应用,实质上是为Spring应用创建并初始化Spring上下文。

执行流程

img

\1. 初始化监听器,以及添加到SpringApplication的自定义监听器。

\2. 发布ApplicationStartedEvent事件,如果想监听ApplicationStartedEvent事件,你可以这样定义:public class ApplicationStartedListener implements ApplicationListener,然后通过SpringApplication.addListener(..)添加进去即可。

\3. 装配参数和环境,确定是web环境还是非web环境。

\4. 装配完环境后,就触发ApplicationEnvironmentPreparedEvent事件。

\5. 如果SpringApplication的showBanner属性被设置为true,则打印启动的Banner。

\6. 创建ApplicationContext,会根据是否是web环境,来决定创建什么类型的ApplicationContext。

\7. 装配Context的环境变量,注册Initializers、beanNameGenerator等。

\8. 发布ApplicationPreparedEvent事件。

\9. 注册springApplicationArguments、springBootBanner,加载资源等

\10. 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

\11. 调用ApplicationContext的refresh()方法,装配context beanfactory等非常重要的核心组件。

\12. 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

\13. 发布ApplicationReadyEvent事件,启动完毕,表示服务已经可以开始正常提供服务了。通常我们这里会监听这个事件来打印一些监控性质的日志,表示应用正常启动了。

SpringBoot会触发其他的一些事件,这些事件按下列顺序触发:

(1)ApplicationStartingEvent:项目刚启动时触发,此时除了注册监听器和初始器之外,其他所有处理都没有开始;

(2)ApplicationEnvironmentPreparedEvent:上下文得到环境信息之后触发,此时上下文创建还没有创建;

(3)ApplicationPreparedEvent:bean的定义信息加载完成之后触发,此时bean还没有初始化;

(4)ApplicationReadyEvent:在所有bean初始化完毕,所有回调处理完成,系统准备处理服务请求时触发;

(5)ApplicationFailedEvent:启动过程出现异常时候触发。

SpringApplication.run()

如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

\1. 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。

\2. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。

\3. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。

\4. 推断并设置main方法的定义类。

它会执行以下步骤:

\1. 创建一个合适的ApplicationContext实例 (取决于classpath)。

\2. 注册一个CommandLinePropertySource,以便将命令行参数作为Spring properties。

\3. 刷新application context,加载所有单例beans。

\4. 激活所有CommandLineRunner beans。

Run启动方式

\1. SpringApplication.run()的底层其实就是new了一个SpringApplication的对象,并执行run()方法。

\2. 有时我们需要创建多层次的ApplicationContext (例如,父子关系的Spring的ApplicationContext 和SpringMVC),这时我们可以使用SpringApplicationBuilder讲多个方法调用串起来,通过parent() 和 child()来创建多层次的ApplicationContext。

启动信息如下:

第 9 行,启动SampleController。

第10行,查找active profile,无,设为default。

第11行,刷新上下文。

第12行,初始化tomcat,设置端口8080,设置访问方式为http。

第13行,启动tomcat服务。

第14行,启动Servlet引擎。

第15行,Spring内嵌的WebApplicationContext 初始化开始。

第16行,Spring内嵌的WebApplicationContext 初始化完成。

第17行,映射servlet,将 dispatcherServlet 映射到 [/] 。

第18行,映射filter,将 characterEncodingFilter 映射到 [/*] 。

第19行,映射filter,将 hiddenHttpMethodFilter 映射到 [/*] 。

第20行,映射filter,将 httpPutFormContentFilter 映射到 [/*] 。

第21行,映射filter,将 requestContextFilter 映射到 [/*] 。

第22行,查找 @ControllerAdvice。

第23行,映射路径 “{[/]}” 到 cn.larry.spring.controller.SampleController.home()。

第24行,映射路径 “{[/error]}” 到 org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)。

第25行,映射路径 “{[/error],produces=[text/html]}” 到 org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)。

第30行,tomcat启动完毕。

第31行,SampleController启动耗费的时间。

第32行,初始化 dispatcherServlet 。

第33行,dispatcherServlet 的初始化已启动。

第34行,dispatcherServlet 的初始化已完成。

第35行,收到shutdown关闭请求。

第36行,关闭AnnotationConfigEmbeddedWebApplicationContext。

SpringBootApplication的启动过程非常复杂,只需要在main方法里调用SpringApplicatio.run()方法即可启动Spring Boot应用。

自动配置原理

image-20220712194812647

整体描述自动配置整个过程:

  1. Spring Boot 通过@EnableAutoConfiguration 注解开启自动配置
  2. 加载 spring.factories 中注册的各种 AutoConfiguration 类
  3. 当某个 AutoConfiguration类满足其注解@Conditional 指定的生效条件(例如Starters 提供的依赖、配置或 Spring 容器中是否存在某个 Bean 等)时,实例化该 AutoConfiguration 类中定义的 Bean(组件等),并注入 Spring 容器。

各个概念:

  • @EnableAutoConfiguration:该注解由组合注解@SpringBootApplication引入,完成自动配置开启,扫描各个jar包下的spring.factories文件,并加载文件中注册的AutoConfiguration类等。
  • spring.factories:配置文件,位于 jar 包的 META-INF 目录下,按照指定格式注册了自动配置的 AutoConfiguration 类。spring.factories 也可以包含其他类型待注册的类。该配置文件不仅存在于 Spring Boot 项目中,也可以存在于自定义的自动配置(或 Starter)项目中。
  • AutoConfiguration 类:自动配置类,代表了 Spring Boot 中一类以 XXAutoConfiguration命名的自动配置类。其中定义了三方组件集成 Spring 所需初始化的 Bean 和条件。
  • @Conditional:条件注解及其衍生注解,在 AutoConfiguration 类上使用,当满足该条件注解时才会实例化 AutoConfiguration 类。
  • Starters:三方组件的依赖及配置,Spring Boot 已经预置的组件。Spring Boot 默认的Starters 项目往往只包含了一个 pom 依赖的项目。如果是自定义的 starter,该项目还需包含 spring.factories 文件、AutoConfiguration 类和其他配置类
@SpringBootApplication 注解的功能

在 Spring Boot 入口类 XXXApplication 中,唯一的一个注解就是@SpringBootApplication。这个注解也没什么特别的,它是通过该注解内组合的@EnableAutoConfiguration 开启自动配置的。

注解中组合了 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan。因此,在实践过程中也可以使用这 3 个注解来替代@SpringBootApplication。

该注解提供了以下成员属性(注解中的成员变量以方法的形式体现):

  • exclude:根据类(Class) 排除指定的自动配置,该成员属性覆盖了@SpringBoot-Application中组合的@ EnableAutoConfiguration 中定义的 exclude 成员属性。
  • excludeName :根据类名排除指定的自动配置,覆盖了@ EnableAutoConfiguration 中的excludeName 的成员属性。
  • :scanBasePackages:指定扫描的基础 package,用于激活@Component 等注解类的初始化。
  • scanBasePackageClasses:扫描指定的类,用于组件的初始化。
  • proxyBeanMethods:指定是否代理@Bean 方法以强制执行 bean 的生命周期行为。此功能需要通过运行时生成 CGLIB 子类来实现方法拦截。该子类有一定的限制,比如配置类及其方法不允许声明为 final 等。

proxyBeanMethods 的默认值为 true,允许配置类中进行 inter-beanreferences (bean 之 间的引用)以及对该配置的@Bean 方法的外部调用。如果@Bean 方法都是自包含的,并且仅提供了容器使用的普通工程方法的功能,则可设置为 false,避免处理 CGLIB 子类。SpringBoot 2.2 版本上市后新增该成员属性,后面章节涉及的自动配置类中基本都会用到proxyBeanMethods,一 般情况下都配置为 false。

通过以上源代码我们会发现,Spring Boot 中大量使用了*@AliasFor 注解,该注解用于桥接到其他注解*,该注解的属性中指定了所桥接的注解类。如果点进去查看,会发现@SpringBootApplication 定义的属性在其他注解中已经定义过了。之所以使用@AliasFor注解并重新在@SpringBootApplication 中定义,更多是为了减少用户使用多注解带来的麻烦。

@EnableAutoConfiguration注解的功能

在未使用 Spring Boot 的情况下,Bean 的生命周期由 Spring 来管理,然而 Spring 无法自动配置@Configuration 注解的类。所以用@EnableAutoConfiguration 注解来允许Spring Boot 根据约定自动管理@Configuration 注解标注的类。

@EnableAutoConfiguration借助@Import的支持,收集和注册依赖包中相关的bean定义。@Import,给容器导入一个Registrar组件

  • @Configuration&、@Bean这两个bean两个注解一起使用就可以创建一个基于java代码的配置类,可以用来替代相应的xml配置文件。
  • @Configuration注解的类可以看作是能生产让Spring IoC容器管理的Bean实例的工厂。
  • @Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册到spring容器中。

自动配置原理

@SpringBootApplication

  • @EnableAutoConfiguration
    • @Import(AutoConfigurationImportSelector.class):*AutoConfigurationImportSelector.class会默认加载 MATE-INF/spring.factories ,以及好多自动配置类*
    • @AutoConfigurationPackage
      • @Import(AutoConfigurationPackages.Registrar.class):*AutoConfigurationPackages.Registrar.class会去加载启动类所在包下面的所有类*
  • @SpringBootConfiguration(本质上是@Configuration注解,随意包装改名了一下)

image-20220712205927356

@EnableAutoConfiguration -》

Springboot 底层实现自动装配的步骤是:

  1. Springboot 应用启动;
  2. @SpringBootApplication 起作用;
  3. @EnableAutoConfiguration;
  4. @AutoConfigurationPackage: 这个组合注解主要是 @Import(AutoConfigurationPackages.Registrar.class), 它通过将 Registrar 类导入到容器中,而 Registrar 类作用是扫描主配置类同级目录及其子包,并将相应的组件导入到 Springboot创建管理容器中
  5. @Import(AutoConfigurationImportSelector.class): 它通过将 AutoConfigurationImportSelector 类导入到容器中,AutoConfigurationImportSelector 类作用是通过selectImports方法执行的过程中,会使用内部工具类SpringFactoriesLoader查找 classpath 上所用 jar 包中的 MATE-INF/spring.factories 进行加载, 实现将配置类信息交给 SpringFactory 加载器进行一系列的容器创建过程。

img

参考:

启动流程

  1. 创建 ApplicationArguments 对象 初始化默认应用参数类。args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:–server.port=9000
  2. 项目运行环境Environment的预配置:并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,并过滤掉不要的env。
  3. 创建Spring容器:根据 webApplicationType 进行判断,确定容器类型。
  4. Spring容器前置处理:容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
  5. 刷新容器:开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化。
  6. Spring容器后置处理:扩展接口,设计模式中的模板方法,默认为空实现。afterRefresh(context, applicationArguments);
  7. 发出结束执行的事件通知:listeners.started(context);
  8. 执行Runners :用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
  9. 发布应用上下文就绪事件

详细流程:

public ConfigurableApplicationContext run(String... args) {
    // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 初始化应用上下文和异常报告集合
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 配置 headless 属性
    configureHeadlessProperty();

    //(1)获取并启动监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        // 创建  ApplicationArguments 对象 初始化默认应用参数类
        // args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        //(2)项目运行环境Environment的预配置
        // 创建并配置当前SpringBoot应用将要使用的Environment
        // 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 排除不需要的运行环境
        configureIgnoreBeanInfo(environment);
        // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
        Banner printedBanner = printBanner(environment);

        // (3)创建Spring容器
        // 根据 webApplicationType 进行判断,确定容器类型,如果为 SERVLET 类型,会反射创建相应的字节码,AnnotationConfigServletWebServerApplicationContext, 接着使用之前传世话设置的context、environment、listeners、applicationArgument 进行应用上下文的组装配置
        context = createApplicationContext();
        // 获得异常报告器 SpringBootExceptionReporter 数组
        //这一步的逻辑和实例化初始化器和监听器的一样,
        // 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
        exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);


        // (4)Spring容器前置处理
        //这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
        prepareContext(context, environment, listeners, applicationArguments,
                       printedBanner);

        // (5):刷新容器
        // 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等),同时向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
        refreshContext(context);

        // (6):Spring容器后置处理
        //扩展接口,设计模式中的模板方法,默认为空实现。
        // 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
        afterRefresh(context, applicationArguments);
        // 停止 StopWatch 统计时长
        stopWatch.stop();
        // 打印 Spring Boot 启动的时长日志。
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // (7)发出结束执行的事件通知
        listeners.started(context);

        // (8):执行Runners
        //用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
        //Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
        //Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    //   (9)发布应用上下文就绪事件
    //表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
    // 这样整个Spring Boot项目就正式启动完成了。
    try {
        listeners.running(context);
    } catch (Throwable ex) {
        // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    //返回容器
    return context;
}

image-20220712200754247

内嵌Tomcat原理

  • spring boot是指出多种服务器启动的,并不只是tomcat,还有jetty等。

  • spring boot的servlet web服务器的配置类位于spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件中的一个名为ServletWebServerFactoryAutoConfiguration的自动配置类。在该自动配置类中,会通过@Import向容器中注入四个配置类。

    在这里插入图片描述

  • EmbeddedTomcat配置类又会向Spring容器注入TomcatServletWebServerFactory,这个类就是Tomcat启动的关键类,用于创建TomcatWebServer。

    在这里插入图片描述

  • 此外,ServletWebServerFactoryAutoConfiguration中还会注入一系列的Customizer,用于修改内嵌Tomcat的参数和配置

    在这里插入图片描述

  • 在Spring的refresh()方法中,有一个onRefresh()方法。默认是一个空的实现,这是留给子类容器实现的扩展方法。在所有的bean定义被注入到容器中之后调用onRefresh()方法。之后,则会对所有的普通单例bean进行实例化和初始化

  • 默认的web服务容器是AnnotationConfigServletWebServerApplicationContext,它又继承了ServletWebServerApplicationContext,该类就对onRefresh方法进行了实现,并且Spring boot的web服务器就是在此启动的!onRefresh()方法内部调用了createWebServer()方法创建web服务器。

  • createWebServer方法的代码,会通过之前配置的ServletWebServerFactory,获取webServer,即创建web服务器。

总体逻辑:

  • spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件引入ServletWebServerFactoryAutoConfiguration自动配置类
  • ServletWebServerFactoryAutoConfiguration自动配置类引入ServletWebServerFactoryAutoConfiguration.EmbeddedTomcat配置类
  • EmbeddedTomcat配置类向Spring容器注入TomcatServletWebServerFactory
  • Spring执行refresh()方法,bean定义注入容器中后,执行onRefresh()方法(空方法,由子类实现),之后执行普通单例bean的实例化和初始化。
  • web容器继承类ServletWebServerApplicationContext执行onRefresh()方法,调用到了createWebServer()方法来创建web服务器。
  • createWebServer()方法通过之前配置的ServletWebServerFactory,获取webServer,即创建web服务器。

转载请注明来源