<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>且听书吟</title>
        <link>https://yufan.me</link>
        <description>诗与梦想的远方</description>
        <lastBuildDate>Tue, 05 May 2026 18:11:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>WordPress 3.2.1</generator>
        <language>zh-CN</language>
        <image>
            <title>且听书吟</title>
            <url>https://yufan.me/logo.svg</url>
            <link>https://yufan.me</link>
        </image>
        <copyright>All rights reserved 2011, 雨帆</copyright>
        <category>文章</category>
        <category>杂谈</category>
        <category>杂思</category>
        <category>编程</category>
        <category>笔记</category>
        <category>小说</category>
        <atom:link href="https://yufan.me/tags/spring/feed" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Spring Boot 简介]]></title>
            <link>https://yufan.me/posts/springboot-brief-intro</link>
            <guid isPermaLink="false">https://yufan.me/posts/springboot-brief-intro</guid>
            <pubDate>Sat, 13 May 2017 04:51:59 GMT</pubDate>
            <description><![CDATA[第一次使用 Springboot 应该是15年年底，当时就被这种约定大于配置的设计惊呆了。那个时候才从上一家公司跳槽，用的是 Spring  3。]]></description>
            <content:encoded><![CDATA[<link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/01.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/02.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/03.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/04.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/05.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/06.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/07.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/08.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/09.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/10.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/11.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/12.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/13.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/14.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/15.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/16.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/17.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/18.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/19.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/20.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/21.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/22.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/23.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/24.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/25.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/26.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/27.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/28.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/29.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/30.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/31.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/32.jpg"><link rel="preload" as="image" href="https://cat.yufan.me/images/slide/spring-boot-intro/33.jpg"><p>第一次使用 Springboot 应该是15年年底，当时就被这种约定大于配置的设计惊呆了。那个时候才从上一家公司跳槽，用的是 Spring 3。所以每次开发新项目的时候，配置起来都让我十分痛苦，也因此喜欢上了 Springboot 的种种便利。</p>
<p>众观 Springboot 的发展，可以发现，其在简化开发上不断地进步。很多常见的组件框架也有了 Springboot 版本。我想，作为 Java 程序员，是时候进入 Springboot 的世界了。</p>
<p>这里分享一份一年前，我在公司内部分享上用的 Slide，以期对于阅读此文的你有所帮助。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/01.jpg?v=1777731763543" alt="" width="2250" height="1688" data-thumbhash="o7gBBYAZd1igiIqBe5eXmeZvrgAZ">
<p>大家好，今天由我来向大家简单介绍一下 Spring Boot 相关的内容，详细的与 Spring Boot 相关的知识将后面由吴一敏同学分享。</p>
<p>首先自我介绍一下，我叫盛宇帆，15年年底加入 OneAPM，现在已经1年多了，目前主要负责和告警引擎相关的开发。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/02.jpg?v=1777731763508" alt="" width="2250" height="1688" data-thumbhash="OegJDYT3V4h7hod7g2mYdThweL/Y">
<p>Spring 官方的博客介绍 Spring： <strong>Spring</strong> is the <strong>“glue”</strong> in your application。我认为 Spring Boot 就是 A glue in Spring Framework。</p>
<p>相信大家都经历过配置 Spring XML 的阶段，十分痛苦地去配置一个 Bean，后面 Spring 3发布之后，基本上很多配置都是通过注解加扫包去配置初始化。
我常见到的一种配置方式，就是和 Spring 框架集成部分的配置，如数据库啊，Web 模板一类的，使用的是 XML，自己项目的 Service、Dao 等类，使用注解初始化。
后面 Spring 4开始流行 @Configuation 注解的配置类初始化配置。</p>
<p>然而，这样子还是十分麻烦，所以才有了 Spring Boot，它给我们最直观的感受，就是 简化了配置。一言一概之，约定大于配置。</p>
<p>然而，仅有这些，并不能说明为何现在 Spring Boot 开始流行，说道 Spring Boot 的兴起，我想起前几天一个技术群的提问：为什么 Spring Boot 应用倾向于打 fat jar 直接启动，而传统的应用倾向于打 war 包从应用容器启动？</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/03.jpg?v=1777731763573" alt="" width="2250" height="1688" data-thumbhash="ctgNDYTqp4V5h4dvhIeHeAR0oa74">
<p>Java 应用部署于应用容器中，其实是受到 J2EE 的影响，也算是 Java Web 有别于其他 Web 快速开发语言的一大特色。一个大大的 war 压缩包，包含了全部的依赖，代码，静态资源，模板。</p>
<p>在虚拟化流行之前，应用都是部署在物理机上的，为了节约成本，多 war 包部署在一个 Servlet 容器内。</p>
<p>但是为了部署方便，如使用的框架有漏洞、项目 jar包的升级，我们会以解压 war 包的方式去部署。或者是打一个不包含依赖的空 war 包，指定容器的加载某个目录，这样所有的war项目公用一套公共依赖，减少内存。当然缺点很明显，容易造成容器污染。</p>
<p>避免容器污染，多 war 部署变为多虚拟机单 war、单容器。</p>
<p>DevOps 流行，应用和容器不再分离，embedded servlet containers开始流行 Spring Boot 在这个阶段应运而生。于是项目部署变为 fat jar + 虚拟机</p>
<p>Docker的流行，开始推行不可变基础设施思想，实例（包括服务器、容器等各种软硬件）一旦创建之后便成为一种只读状态，不可对其进行任何更改。如果需要修改或升级某些实例，唯一的方式就是创建一批新的实例以替换。</p>
<p>基于此，我们将配置文件外置剥离，由专门的配置中心下发配置文件。</p>
<p>这也是我们为何要学习和使用 Spring Boot 的背景，我觉得这才是 Spring Boot 开始流行的主要原因。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/04.jpg?v=1777731763605" alt="" width="2250" height="1688" data-thumbhash="PfgFDYJwmJmOdoekeeholHhwf/gI">
<p>总的来说 Spring Boot 有以下几个特点。</p>
<ol>
<li>配置简化，这个我印象最深刻的就是写 MyBatis 的时候，一堆东西要配置，一般大家都会用那个 Generator 去生成。而实际上 Spring Boot 推崇 jpa，如果只是简单的 CRUD，用 Spring Boot + JPA 的方式简单到只需要几行关于数据库的配置就好了。</li>
<li>自动配置机制，很多教程都称它为 Magic，基于项目的某些条件，自动初始化装配必要的 Bean，稍后会在后面的演示中详解。</li>
<li>Starter 本质上就是 Spring 基于 Gradle 和 Maven 这两种构建工具定义的一组依赖，一般是按照功能或者框架划分。在有了自动配置的机制下，我们只需要依赖 Starter 指定的坐标，和非常简单的属性配置即可集成我们想要的框架。</li>
<li>嵌入的 Servlet 容器，主要是为了方便部署的。</li>
<li>主要是 <code>spring-boot-starter-actuator</code> 和 <code>spring-boot-starter-remote-shell</code> 的使用，当然，这里还可以使用 JMX 一类的做管理，大家可以参考文档。（2017年更新 remote shell 已经废弃）</li>
</ol>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/05.jpg?v=1777731763636" alt="" width="2250" height="1688" data-thumbhash="8PcNDYDnt3iEeYh/dPZXdqhyf6+Y">
<p>首先，我们来用 Spring Boot 写一个 Hello World吧，这个是仿照 Spring 官方的示例代码改的，使用 Groovy，所以连 import 都省了。这里主要是为了演示一个最简单的 Spring Boot 应用，通过下面的这行命令我们就能把它启动了。</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0" icon="&lt;svg viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M 6,1 C 4.354992,1 3,2.354992 3,4 v 16 c 0,1.645008 1.354992,3 3,3 h 12 c 1.645008,0 3,-1.354992 3,-3 V 8 7 A 1.0001,1.0001 0 0 0 20.707031,6.2929687 l -5,-5 A 1.0001,1.0001 0 0 0 15,1 h -1 z m 0,2 h 7 v 3 c 0,1.645008 1.354992,3 3,3 h 3 v 11 c 0,0.564129 -0.435871,1 -1,1 H 6 C 5.4358712,21 5,20.564129 5,20 V 4 C 5,3.4358712 5.4358712,3 6,3 Z M 15,3.4140625 18.585937,7 H 16 C 15.435871,7 15,6.5641288 15,6 Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt;"><code class="language-java"><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> GreetingRestController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&quot;/hi/{name}&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    def </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hi</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        [ greeting</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> &quot;Hello, &quot;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> &quot;!&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/06.jpg?v=1777731763666" alt="" width="2250" height="1688" data-thumbhash="PvgFDYKXl3iOh3d0d9d4cHhwf/gI">
<p>现在，问题来了，刚才那个项目那么简单，那个，整个项目的启动过程中，到底发生了哪些魔法呢？</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/07.jpg?v=1777731763698" alt="" width="2250" height="1688" data-thumbhash="nOcVDYKYl5d4h3d/h4d3fIh1n/u5">
<p>我们将 Groovy 的代码翻译为 Java版本，大概会看到，一个项目想要启用 Spring Boot，关键在于 <code>SpringApplication</code> 类和 <code>EnableAutoConfiguration</code> 注解的使用。</p>
<p>SpringApplication 是 Spring Boot 提供的用于 Java main 方法的启动类。它的执行操作首先为：</p>
<ol>
<li>Create an appropriate ApplicationContext instance (depending on your classpath)</li>
<li>Register a CommandLinePropertySource to expose command line arguments as Spring properties</li>
<li>Refresh the application context, loading all singleton beans Trigger any CommandLineRunner beans</li>
</ol>
<p>然后 <code>EnableAutoConfiguration</code> 则为 Enable 类注解这里通过此注解，告诉 Spring Boot 开启自动装配的特性。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/08.jpg?v=1777731763728" alt="" width="2250" height="1688" data-thumbhash="PPgFDYJxqZmMh3eWaPdnhnhwf/gI">
<p>除了 <code>EnableAutoConfiguration</code>，我们常常和它并列使用的还有 <code>ComponentScan</code> <code>Configuration</code> 注解。这三个注解合起来，有一个等价的注解，叫做 <code>SpringBootApplication</code>，一般在我们的项目开发中，喜欢在项目最外面的包下面创建包含 main 方法的程序启动类，然后这个类上标记为 @SpringBootApplication 这个注解，这样就等价于基于 main 方法类所在的 package 为 Spring 扫包的基础包路径，且开启自动化配置。</p>
<p>自动化配置的实现，不得不说 Spring Boot 本质上是通过 Conditional 类注解来实现的。</p>
<p><code>@ConditionalOnClass</code> 表示对应的类在classpath目录下存在时，才会去执行注解所标示的自动配置类或者自动配置方法，与之对应的我们就@ConditionalOnMissingClass 注解，也就是找不到对应的类的时候。</p>
<p><code>@ConditionalOnBean</code> 和 <code>@ConditionalOnMissingBean</code> 则同样很容易按照字面意思理解。</p>
<p>当然 <code>Conditional*</code> 注解不仅仅上面提到的这些，还有 <code>ConditionalOnExpression</code> 一类的。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/09.jpg?v=1777731763769" alt="" width="2250" height="1688" data-thumbhash="nucZFYB4h4iFd3d/iIiHenh0r8r6">
<p>这个是我们从 Spring Boot 当中节选的一段代码，主要是为了演示自动化配置的具体实现。首先我们在项目配置里面标明 spring.jmx.enabled = true，ConditionalOnProperty 注解生效，然后 Spring 发现能找到 <code>MBeanExporter.class</code> 这个类，于是开始执行自动化配置的方法，因为这个时候项目中没有定义 <code>MBeanExporter</code> 这个 Bean，于是 <code>ConditionalOnMissingBean</code> 注解生效，Spring 开始读取配置属性，自动创建此 Bean 对象。</p>
<p>同理，任何一个自动装配的实现，基本上就是组合这些条件注解。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/10.jpg?v=1777731763799" alt="" width="2250" height="1688" data-thumbhash="PPgFDYKAiJiPh3aBiehodXhwf/gI">
<p>当然，如果能被 Spring Boot 官方直接支持的话是最吼的，目前被支持的肯定不止上面这些，我只是简单地列举了一些常见的项目。</p>
<p>Spring Boot 官方之前发起过好几次投票，就是列举一些框架，然后大家投票选择哪些想要被官方支持的。上半年的时候，我还在里面看到了之前姜老师维护的 camel，然而似乎并没有被选中。MyBatis 目前虽然有 Spring Boot 版，但是是由 MyBatis 团队自行维护，至少我6月份尝试使用的时候，问题还是蛮多的。 (2017年之后的版本基本可用，主要是有了 Boot 版本的 VFS)</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/11.jpg?v=1777731763829" alt="" width="2250" height="1688" data-thumbhash="H/gVBYKIh4eEeHh/d5d4eU+Qe/lJ">
<p>上面是我从 Spring Boot 1.3.6 中找到的 spring.factories 文件的截图。当然，基本上只要上面有的，都能得到不错的支持。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/12.jpg?v=1777731763859" alt="" width="2250" height="1688" data-thumbhash="PPgFDYJxqYl/hoiVaLeJdohyf/ko">
<p>前面我们简单介绍了自动配置的实现原理，基本上流程就是配置文件标明启用什么服务，然后找到对应的依赖（class），然后结合条件装配初始化 Bean。</p>
<p>所以 Spring 就更进一步，按照功能模块，划分出一个个 Starter 模块。以 Maven 为例，基本上我们只需要将 Spring Boot 自己的那个 POM 文件设置为 parent，然后依赖中直接依赖所需的 Starter 坐标，即可依赖所有所需的 jar 包，剩下的的东西，仅有最基础的属性值配置。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/13.jpg?v=1777731763896" alt="" width="2250" height="1688" data-thumbhash="3PcVDYJ3h3iHd3d/iIh4fIhzf/lI">
<p>当然 Starter 也是一把双刃剑，比如我在项目里面依赖了 <code>spring-boot-starter-data-jpa</code> 之后在 IDEA 里面看到的依赖树，简直就是 jar 包狂魔，虽然我们需要 jpa，但是不一定需要全部这些包。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/14.jpg?v=1777731763932" alt="" width="2250" height="1688" data-thumbhash="PPgFDYKVl4hoh4h3h/hodHhwf/gI">
<p>这也就引出了我使用 Starter 的时候的几个困扰。比如项目需要以 spring-boot 的 pom 为 parent，这个就比较讨厌了，尤其是我前公司，所有的项目是同一内部的 parent，这样子可以管理大家的依赖。如果要用Spring Boot 的话，就会略有麻烦。可能就需要通过依赖 Spring Boot 的 pom 的方式，并不是很优雅。</p>
<p>问题二是我在用 Spring Boot 时依赖 logstash 遇到的， logstash自己依赖了一个 logback-access 和那个版本的 Spring Boot 依赖的 logback 不一致，导致一直报一个 <code>java.lang.AssertionError</code></p>
<p>问题三就是我最近想用 Spring Boot 去读写 Kafka，结果我们用的 Kafka 版本比较老，最后只好自己配置，特别麻烦。很多老的组件，要么你得用老的 Spring Boot，要么你就得自己配置。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/15.jpg?v=1777731763965" alt="" width="2250" height="1688" data-thumbhash="IQgSBYDQpnmOlXiLhpiIh/6m+Lom">
<p>比如我们基于 YAML 定义了上图这么一段配置，最简单的方式就是 <code>@Value</code> 注解，通知这货还支持 Spring El 表达式，做一些简单的处理判断。</p>
<p>但是对于一个组件的配置，或者是项目自己的配置，更需要比较好的梳理，Spring Boot 便支持了所谓的 prefix 前缀的概念，我们可以把所需要的配置信息定义为一个配置类，在里面定义好必要的 Getter Setter 一类的东西。在初始化项目的时候使用 <code>@EnableConfigurationProperties</code> 注解即可实现配置参数注入到配置类里面。</p>
<p>当然麻烦的地方在于如果配置文件定义的层级过深，配置类会变得极其复杂。建议这种情况下，尽可能简化层级。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/16.jpg?v=1777731764002" alt="" width="2250" height="1688" data-thumbhash="+vcFBYKgqpZ/h4eFeOhXk6tGf/kY">
<p>配置文件的加载，其实 Spring Boot 有一个非常复杂的流程，大家好奇的话可以看 Spring Boot 文档中的定义，大概有十几种情况。但是大部分情况下，我们用不了这么多，上面是我认为应该知道并且利用的几种情况，配置加载的顺序是由上往下。</p>
<p>第一种情况，和 jar 包在同一目录下，一般是应用发布到生产，然后还想修改更新配置的情况。</p>
<p>项目 resources 目录和 resources/config 下面的配置文件，就是我们在开发的时候会选取的存放配置的位置。</p>
<p>当然配置文件的名称默认是 application，还可能根据你所启用的 Profile 加载不同名称的配置文件。</p>
<p>由于配置文件的指定在 Spring Boot 中极其灵活，（官方可能把所有的情况都考虑到了）所以大家可以自己按需选择。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/17.jpg?v=1777731764034" alt="" width="2250" height="1688" data-thumbhash="PPgFDYKTlpmLd4d2h/hmhXhwf/gI">
<p>测试当然也有 Starter，我们只需要依赖 <code>spring-boot-starter-test</code>，即可轻松写测试。常见的测试注解就是上面几个</p>
<p><code>@WebIntegrationTest</code> 注解相当于 <code>@IntegrationTest</code> + <code>@WebAppConfiguration</code> 注解结合使用，在 1.3 之前，主要是使用前面4个注解进行测试。</p>
<p>1.4 之后，我们主要使用 <code>SpringBootTest</code> 注解做测试</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/18.jpg?v=1777731764073" alt="" width="2250" height="1688" data-thumbhash="6PcNDYCTepemZ4iNd/iFr4aTT/ts">
<p>最老的方式，你可能会使用 <code>@ContextConfiguration</code> 注释和 <code>SpringApplicationContextLoader</code> 的组合去写单元测试。</p>
<p>当然，我们可以去掉 loader 的配置，使用方法2 的 <code>SpringApplicationConfiguration</code> 注解去测试</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/19.jpg?v=1777731764152" alt="" width="2250" height="1688" data-thumbhash="7/cRDYKQi5eJd4iqZ5d4q4KEL/tJ">
<p>当想写一个集成测试的时候，可以使用 <code>IntegrationTest</code> 注解，和前面的不一样的是，前面两种方式不会初始化全部的 Bean，而 <code>IntegrationTest</code> 会和生产环境一样，完整初始化程序。但是它不会初始化 嵌入式的 Servlet 容器。</p>
<p>当你需要嵌入式的 Servlet 容器做一些接口的集成测试的时候，就需要使用 <code>WebIntegrationTest</code> 注解</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/20.jpg?v=1777731764118" alt="" width="2250" height="1688" data-thumbhash="8vcRDYC3p5h1eIh/heZndJhzj/6p">
<p>然而一个项目，基本上包含 <code>SpringApplication</code> 和 main 方法的类只有一个，所以在1.4 之后，我们连 App.class 都不需要给定，直接使用 <code>@SpringBootTest</code> 注解即可，更加优雅。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/21.jpg?v=1777731764192" alt="" width="2250" height="1688" data-thumbhash="FPgNDYKId3eFiHd/iHh4fb21j/kZ">
<p>这个就是我基于 Spring Boot 1.3 写的一个集成测试，当然它使用的是我们前面说的方法3。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/22.jpg?v=1777731764225" alt="" width="2250" height="1688" data-thumbhash="PfgFDYKFh4d5iHd0iPhoc3hwf/gI">
<p>前面我们其实已经说到了 Spring Boot 的 <code>Profile</code> 可以让我们区分不同环境下加载的配置文件。比如开发环境，测试和线上，很多值都可以实现定制，而不需要重新打包项目。</p>
<p>使用 Profile 的第二个场景就是不同的 Profile 需要初始化不同的bean，比如以 DataSource 为例，测试的时候，因为测试环境不一样，我们更期望 DataSource 能用 H2一类的嵌入式数据库模拟。开发和生产环境，就需要初始化一个 MySQL 的 DataSource。当然我说的这个场景不需要我们专门去配置，因为 Spring Boot 已经替我们考虑到了这种情况，在需要 DataSource，但是没有这个 Bean，切依赖了 H2的 Driver 的时候，Spring 会自动创建一个 H2 的 DataSource。</p>
<p>还有一个我使用 Profile的场景就是 Swagger，它十分好用，尤其是开发的时候能基于注解自动生成 API Doc，和测试页面，然而，会存在的问题就是它有漏洞，我只希望在开发的时候启用 Swagger，这个时候就可以利用 Profile 来实现。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/23.jpg?v=1777731764259" alt="" width="2250" height="1688" data-thumbhash="PfgBDYKjqomPhniDZ8d2cXhwf/gI">
<p>Profile 可以启用一个或者多个，然而，也会导致一些问题，比如我们没有指定 Profile 的时候怎么办，或者我们有 profile 名为 mysql、cassadra。它们是相互 block 的，如何检查校验，避免冲突的 profile 同时启用呢？</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/24.jpg?v=1777731764294" alt="" width="2250" height="1688" data-thumbhash="HPgVDYKIh4h2h4d/eJiHe3qBf+kI">
<p>这个是添加默认 Profile 的方式，原来我是尝试在 application 配置文件里面设置，但是不生效，最后我只好手动编码实现。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/25.jpg?v=1777731764333" alt="" width="2250" height="1688" data-thumbhash="IPgdDYJ3h4eJh4h/iIh3enmAf/oo">
<p>这个方法是和 main 方法同级的一个方法，需要 Autowired Spring 的 Environment 接口，然后获取 Profile 的配置，即可自行实现判断逻辑。（期待后面 Profile 能更加完善，实现 Block 一类的属性）</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/26.jpg?v=1777731764363" alt="" width="2250" height="1688" data-thumbhash="OfgFDYKXh4h/h3d2d5h4e3hwfPhI">
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/27.jpg?v=1777731764398" alt="" width="2250" height="1688" data-thumbhash="O/gFDYKUp5h7hoiMh9d4YHhwf/gI">
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/28.jpg?v=1777731764430" alt="" width="2250" height="1688" data-thumbhash="PfgFDYJimJlrl4hxiPhocnhwf/gI">
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/29.jpg?v=1777731764462" alt="" width="2250" height="1688" data-thumbhash="JvgdDYCHh3d4h3d/iIh3dnlwfvkI">
<p>Spring Boot 官方推崇的部署方式是 jar，原因我们前面也分析过了，但是也会存在打包为 war 去部署的需求。这里我们只需要在和 Application.class 同级的路径下继承 SpringBootServletInitializer 类去定义一下 SpringApplicationBuilder 的配置即可，还是很轻松的。我这个截图的示例里面用了前面设置默认 Profile 的方法，重用了一下代码。</p>
<p>有了这么一个类之后，我们就可以在 pom 里面设置项目打包为 war，它既能 java –jar去执行这个 war，也能直接丢到 Tomcat 一类的容器里面运行。</p>
<p>我们在 17年的实践中发现，很多时候，非 Fatjar 也有一定的意义，于是有了下属的打包启动实践，大家可以去参考。</p>
<p><a href="https://gist.github.com/syhily/c66310c150653e8f92b9fa6693df8207" rel="nofollow" target="_blank">https://gist.github.com/syhily/c66310c150653e8f92b9fa6693df8207</a></p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/30.jpg?v=1777731764491" alt="" width="2250" height="1688" data-thumbhash="PfgFDYCUiId8h4eEd/hocYZwP/oY">
<p>如果想要快速创建一个 Spring Boot 项目开发，有且不仅有上述几种方式。</p>
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/31.jpg?v=1777731764522" alt="" width="2250" height="1688" data-thumbhash="PfgFDYJymJlrl3iTifdocnhwf/gI">
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/32.jpg?v=1777731764552" alt="" width="2250" height="1688" data-thumbhash="PPgBDYKQmohthYiUaPeJg3hwf/gI">
<img src="https://cat.yufan.me/images/slide/spring-boot-intro/33.jpg?v=1777731764684" alt="" width="2250" height="1688" data-thumbhash="eOgFDYK4R7eNd3hvdXpZd6ZgkvLq">
<p>你可以点击 <a href="https://cat.yufan.me/uploads/springboot-intro.pdf" rel="nofollow" target="_blank">这里</a> 下载到本地浏览。</p>]]></content:encoded>
            <author>syhily@gmail.com (雨帆)</author>
            <category domain="https://yufan.me/tags/spring">Spring</category>
            <category domain="https://yufan.me/tags/speech">演讲</category>
            <category domain="https://yufan.me/cats/coding">编程</category>
            <enclosure url="https://yufan.me/images/og/springboot-brief-intro.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>