添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Spring Boot takes an opinionated view of building production reday applications.
Spring Boot 具有一套固化的视图,该视图用于构建生产级别的应用

Spring Boot 六大特性

  • 创建独立的 Spring 应用
  • 直接嵌入 Tomcat、Jetty 或 Undertow 等 Web 容器,无需部署 WAR 文件
  • 提供固化的 starter 依赖,简化构建配置
  • 当条件满足时自动装配 Spring 或第三方类库
  • 提供运维(Production-Ready)特性,如指标信息、健康检查及外部配置
  • 绝无代码生成,且无需 XML 配置

  • 准备运行环境
  • 理解独立的 Spring 应用

    创建 Spring Boot 应用

    运行 Spring Boot 应用

  • 可执行 Jar 资源结构
  • Fat Jar/War 执行模块 —— spring-boot-loader
  • JarLauncher 实现原理

    注册自定义协议处理器 JarFile.registerUrlProtocolHandler()

    利用 java.net.URLStreamHandler 扩展机制 ,即在 Java 系统属性 java.protocal.handler.pkgs 追加注册 Spring 自定义协议处理器所在的包名 org.springframework.boot.loader ,从而使得 URL.getURLStreamHandler(String) 获得 Spring 的处理器 org.springframework.boot.loader.jar.Handler 进行 Fat Jar 的资源加载及运行。

    与 JarLauncher 类似,WarLauncher 对应处理器与前者相同,只是在包中的 BOOT-INF 变更为 WEB-INF , 同时为了兼顾外部 Web 容器的部署时依赖包冲突,新加入 WEB-INF/lib-provided 目录,把在 POM 文件中的标识 <scope>provided</scope> 的依赖包放入其中,该目录的包只在内嵌 Web 容器时生效,例如:Servlet API 包即可声明为 provided 放入该目录,当外部部署时的 Web 容器已经提供此包,从而防止引入冲突。

    spring-boot-starter-parent

    该 starter 有别与一般的 Spring Boot Starter 项目,它是专门用来管理 Spring Boot 依赖、默认配置一些插件,如:maven-jar-plugin 等等。开发人员通过将其指定为父 POM 能快速构建 Spring Boot 应用程序。指定方式为在项目的 pom.xml 文件增加如下声明:

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.2.13</version>
      </parent>
        

      它的打包方式为 POM(Project Object Model)它的父 POM 为 spring-boot-dependencies

    spring-boot-dependencies

      由上可知,它是 spring-boot-starter-parent 的父 POM,是完成管理 Spring Boot 依赖默认配置一些插件的真正 POM,当 Spring Boot 项目 POM 有自定义的父 POM 时,无法通过指定 spring-boot-starter-parent 为父 POM 时,可以直接在项目 pom.xml 文件中引入这个依赖实现 Spring Boot 依赖管理及默认配置必要插件:

      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-dependencies</artifactId>
                  <version>2.2.13.RELEASE</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
        

    spring-boot-starter-parent 具体内容,请查阅 版本 2.2.13.RELEASE,可以看出其父 POM 声明:

        <!-- 注意:此处为父 POM 的声明 -->
        <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-dependencies</artifactId>
          <version>2.2.13.RELEASE</version>
          <relativePath>../spring-boot-dependencies</relativePath>
        </parent>
        

    spring-boot-dependencies 具体内容,请查阅 版本 2.2.13.RELEASE

    理解嵌入式 Web 容器

      Spring Boot 1.0 版本应用支持的直接嵌入的 Web 容器有:Tomcat、Jetty 和 Undertow,Spring Boot 2.0 版本则开始支持嵌入式 Reactive Web 容器,默认实现为 Netty Web Server。不过由于 Spring WebFlux 基于 Reactor 框架实现,因此在 Spring Boot 中,Netty Web Server 属于 Reactor 与 Netty 的整合实现,由 spring-boot-starter-webflux 的依赖 spring-boot-starter-reactor-netty 间接引入。而前面表格提及的三个嵌入式 Servlet 容器也能作为 Reactive Web Server,并允许替换默认实现 Netty Web Server。这是因为支持 Servlet 3.1+ 规范的容器也开始支持 Reactive 异步非阻塞的特性。

    嵌入式 Servlet Web 容器

      Spring Boot 通过引入 spring-boot-starter-web 依赖来支持 Servlet Web 项目,默认的嵌入式 Servlet Web 容器为 Tomcat,可以通过 Maven 依赖排除方式移除默认容器,另外引入其他 Servlet Web 容器。

    Tomcat 作为嵌入式 Servlet Web 容器

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
        

    Jetty 作为嵌入式 Servlet Web 容器

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <exclusions>
              <!-- 剔除默认 Tomcat 依赖 -->
              <exclusion>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-tomcat</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
      <!-- 引入 Jetty 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-jetty</artifactId>
      </dependency>
        

    UndertowServletWebServer 作为嵌入式 Servlet Web 容器

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <exclusions>
              <!-- 剔除默认 Tomcat 依赖 -->
              <exclusion>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-tomcat</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
      <!-- 引入 Undertow 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-undertow</artifactId>
      </dependency>
    

    嵌入式 Reactive Web 容器

      Spring Boot 2.0 开始支持嵌入式 Reactive Web 容器,但其处于被动激活状态,需要增加 spring-boot-starter-webflux 依赖,并且依赖中不能出现 spring-boot-starter-web 依赖。一旦两者同时存在时, spring-boot-starter-webflux 会被忽略,导致项目还是使用嵌入式 Servlet Web 容器,而非 Reactive Web 容器。这个机制是由 SpringApplication 实现中的 Web 应用类型(WebApplicationType)推断逻辑决定的。

      本节开头提及:

    Spring Boot 2.0 版本则开始支持嵌入式 Reactive Web 容器,默认实现为 Netty Web Server。不过由于 Spring WebFlux 基于 Reactor 框架实现,因此在 Spring Boot 中,Netty Web Server 属于 Reactor 与 Netty 的整合实现,由 spring-boot-starter-webflux 的依赖 spring-boot-starter-reactor-netty 间接引入。

      换言之,当 Reactive 型 Web 应用引入 spring-boot-starter-webflux 依赖时,默认会提供一个基于 Reactor 与 Netty 整合实现的 Netty Web Server,进一步查询 spring-boot-autoconfigure 中关于 Reactive Web Server 的自动装配配置类 ReactiveWebServerFactoryConfiguration 还能发现,类资源路径中一旦发现其他嵌入式 Web 容器,此默认的 Netty Web Server 将被替换。

    // 此处是默认 Netty 容器工厂类自动配置
    @Configuration
    @ConditionalOnMissingBean(ReactiveWebServerFactory.class)
    @ConditionalOnClass({ HttpServer.class })
    static class EmbeddedNetty {
        @Bean
        public NettyReactiveWebServerFactory nettyReactiveWebServerFactory() {
            return new NettyReactiveWebServerFactory();
    // 此处是默认 Tomcat 容器工厂类自动配置
    @Configuration
    @ConditionalOnMissingBean(ReactiveWebServerFactory.class)
    @ConditionalOnClass({ org.apache.catalina.startup.Tomcat.class })
    static class EmbeddedTomcat {
        @Bean
        public TomcatReactiveWebServerFactory tomcatReactiveWebServerFactory() {
            return new TomcatReactiveWebServerFactory();
    // Jetty 及 Undertow 的自动配置雷同,此处省略
    

      可以看出不同容器的工厂是否装载都是互斥的,通过条件注解 @ConditionalOnMissingBean(ReactiveWebServerFactory.class) 实现,那么问题来了,当依赖中存在多个 Web 容器时,会优先采用哪个呢?

      其实这就要看这一组互斥的 ReactiveWebServerFactory Bean 哪个先注册,翻看 ReactiveWebServerFactoryAutoConfiguration 类的 @Import 注解代码,答案很明显优先级为:Tomcat > Jetty > Undertow > Netty

    @Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    		// 优先 Tomcat 的装载
    		ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,
    		ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,
    		ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,
    		ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class })
    public class ReactiveWebServerFactoryAutoConfiguration {
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-webflux</artifactId>
      </dependency>
        

    UndertowWebServer 作为嵌入式 Reactive Web 容器

      <!-- 引入 WebFlux 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-webflux</artifactId>
      </dependency>
      <!-- 引入 Undertow 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-undertow</artifactId>
      </dependency>
        

    注意区别:

  • UndertowWebServerReactive Web 容器
  • UndertowServletWebServerServlet Web 容器
  • 在引入 Undertow 时而不需要在 spring-boot-starter-webflux 中 exclusion spring-boot-starter-reactor-netty 有两方面原因:
  • 上面提及的 @Import 自动装配优先级里,Netty 优先级最低,不干扰其他 Web 容器的引入
  • 使用 WebClient 时需依赖 Netty 实现,即便已引入其他 Web 容器也可共存
  • <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <!-- 引入 Jetty 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>

    Tomcat 作为嵌入式 Reactive Web 容器

      <!-- 引入 WebFlux 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-webflux</artifactId>
      </dependency>
      <!-- 引入 Tomcat 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
      </dependency>
        

    @ComponentScan 负责激活对 @Component 组件的扫描,从 Spring Boot 2.0 版本后并非使用默认值,而是设置了 excludeFilters 属性值,包含 TypeExcludeFilterAutoConfigurationExcludeFilter 类型的两个过滤注解 @Filter。前者用于 Bean 注册时判断某个 Bean 是否被排除,后者用于排除同时标注 @Configuration 和 @EnnableAutoConfiguration 的类。

    @SpringBootConfiguration 声明被标注类为配置类,它是从 Spring Boot 1.4 开始引入,它由元注解 @Configuration 注解(称作注解 派生)而 1.4 之前 @SpringBootApplication 一直由 @Configuration 直接注解。

    @EnableAutoConfiguration 负责激活 Spring Boot 自动装配机制。

    @SpringBootApplication 属性别名

      @SpringBootApplication 注解属性方法中,很多被 @AliasFor 注解标注,它用于桥接其他注解的属性,例如:

    * 属性 scanBasePackages 是桥接 ComponentScan 注解的 basePackages 属性 * 称这种桥接为设置别名 * 值得注意:如果此属性没被设置,默认扫描包路径为此注解被标注的类所在的包路径 @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {};

    @SpringBootApplication 继承 @Configuration 的 CGLIB 提升特性

      有别于 @Bean、@Componet 等注解,被 @Configuration 注解的类是被 CGLIB 增强的对象,使得在增强类中被 @Bean 注解的属性及方法被引用或调用时,得到的对象时同一个实例。官方称这种在 @Configuration 下声明的 @Bean 为 完全模式(Full),相反在非 @Configuration 标注的普通类下声明的 @Bean 为 轻量模式(Lite) 。了解此机制对某些场景 Bean 行为不符合预期时可以进行解释,例如:类中某个方法明明声明了事务 @Transaction,但在此类中直接调用时发生异常不回滚,多半是采用了轻量模式的方法调用,应该采用 CGLIB 增强的对象进行方法调用。

    处理 @Configuration 注解的类的 CGLIB 提升,详见 Spring @Enable 模块驱动原理

    理解自动配置机制

      Spring Boot 1.0 在 Spring Framework 4.0 基础上,添加了约定配置化导入 @Configuration 类的方式,使得自动装配 @Configuration 类得以实现。这种自动化加载的方式被称作 工厂加载机制(Factory Loading Mechanism),也可称为 Spring 形式的 SPI。该机制通过结合条件注解 @Conditional 达到更灵活的条件化装配。

      具体实现自动装配的大致步骤:

  • 定义一个标注 @Configuration 的配置类 WebAutoConfiguration
  • 在项目路径新建资源文件 src/main/resources/META-INF/spring.factories
  • 文件以键值对形式的出现,键为自动装配激活注解 @EnableAutoConfiguration 的全限定名,值为配置类全限定名

    # 自动装配配置类设置
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    thinking.in.spring.boot.autoconfigure.WebAutoConfiguration
        

    项目可扫描的路径类中使用 @EnableAutoConfiguration 激活自动装配,即可实现自定义自动装配

    // 激活自动装配
    @EnableAutoConfiguration
    public class FirstAppByGuiApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(FirstAppByGuiApplication.class, args);
      
  • 使用场景:监视及管理运行中的应用
  • 监管媒介:HTTP 或 JMX 端点(Endpoints)
  • 端点类型:审计(Auditing)、健康(Health)、指标收集(Metrics Gathering)
  • 基本特点:自动运用(Automatically Applied)
  • 参考阅读:What Is an Actuator?

    Spring Boot Actuator Endpoints

    添加依赖:

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    Spring Boot Actuator 主要包含如下端点:

  • beans 显示 Spring 应用上下文完整 Bean 列表(所有 ApplicationContext 层次)
  • conditions 显示应用所有配置类和自动装配类的条件评估结果(包含匹配及未匹配)
  • env 暴露 Spring ConfigurableEnvironment 中的 PropertySource 属性
  • health 显示应用健康信息
  • info 显示任意引用信息
  • 默认仅显示 health、info 两个 Web 端点,指定属性 management.endpoints.web.exposure.include=* 暴露所有 Web 端点。

    访问 Web 端点方式:http://IP:Port/actuator/endpointName,其中 endpointName 替换成具体的端点

    理解外部化配置

      Spring Boot 通过资源文件(Properties)、YAML 文件、环境变量或者命令行参数形式实现外部化配置属性值,而这些属性值可以被以下几种方式被 Spring Boot 应用所引用获取:

  • Bean 的 @Value 注解注入
  • Spring Enviroment 读取
  • @ConfigurationProperties 绑定到结构化对象
  • 参考阅读:外部配置优先级

    理解规约大于配置

      Spring Boot 规约大于配置的字面理解:期间绝对无代码生成、无须 XML 配置。换言之 Spring Boot 尽管功能强大,但是能通过一些规约达到不生成中间代码、不需要 XML 配置的情况下达到开箱即用。Spring Boot 能完成这点,从技术角度看是借助 Spring Framework 基础设施的完善结果。具体 Spring Framework 从 XML 文件驱动 走向 注解驱动 的演进过程参考如下表:

    @Component
    <context:component-scanbasePackage=”…“> XML 与 @Component 结合阶段 XML 指定扫描范围扫描 @Component 组件 @Bean
    @Import
    @Configuration
    @Bean 代替 <bean>
    @Configuration 部分替代 XML
    <context:component-scan> 需硬编码指定 Bean 扫描范围、部分摆脱 XML @ComponentScan
    ImportSelector 接口
    @EnableXxx 模块激活注解 全面走向注解 能部分选择性加载配置 @Conditional
    spring.factories 工厂加载机制 全面走向注解 灵活的 @Conditional 条件注解驱动的自动化装配

      Spring Framework 为 Spring Boot 提供核心技术支持,使得 Spring Boot 具备完整的微服务各项功能。而 Spring Cloud 将 Spring Boot 当做分布式系统中微服务的中间件,并结合自身的分布式核心特性,最终给开发者带来完整且强大的分布式系统解决方案。

    《Spring Boot 编程思想(核心篇)》中篇 —— 走向自动装配

    《Spring Boot 编程思想(核心篇)》下篇 —— 理解 SpringApplication