(window.webpackJsonp=window.webpackJsonp||[]).push([[409],{840:function(t,e,r){"use strict";r.r(e);var o=r(56),a=Object(o.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"用-spring-boot-开发"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#用-spring-boot-开发"}},[t._v("#")]),t._v(" 用 Spring boot 开发")]),t._v(" "),r("p",[t._v("本节将更详细地介绍如何使用 Spring 引导。它涵盖了诸如构建系统、自动配置以及如何运行应用程序等主题。我们还介绍了一些引导最佳实践。虽然 Spring boot 并没有什么特别之处(它只是另一个你可以使用的库),但有一些建议,如果遵循这些建议,将使你的开发过程变得更容易一些。")]),t._v(" "),r("p",[t._v("如果你是从 Spring boot 开始的,那么在深入讨论这一部分之前,你可能应该阅读 "),r("em",[r("RouterLink",{attrs:{to:"/spring-boot/getting-started.html#getting-started"}},[t._v("开始")])],1),t._v(" 指南。")]),t._v(" "),r("h2",{attrs:{id:"_1-建立系统"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_1-建立系统"}},[t._v("#")]),t._v(" 1. 建立系统")]),t._v(" "),r("p",[t._v("强烈建议你选择一个支持"),r("a",{attrs:{href:"#using.build-systems.dependency-management"}},[t._v("* 扶养管理 *")]),t._v("并且可以使用发布到“ Maven Central”存储库的工件的构建系统。我们建议你选择 Maven 或 Gradle。让 Spring 引导与其他构建系统(例如 Ant)一起工作是可能的,但是它们没有得到特别好的支持。")]),t._v(" "),r("h3",{attrs:{id:"_1-1-依赖管理"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_1-1-依赖管理"}},[t._v("#")]),t._v(" 1.1.依赖管理")]),t._v(" "),r("p",[t._v("Spring 启动的每个版本都提供了它所支持的依赖关系的精心策划的列表。在实践中,你不需要在构建配置中为这些依赖项中的任何一个提供版本,因为 Spring boot 为你管理这些依赖项。当你升级 Spring 引导本身时,这些依赖关系也会以一致的方式进行升级。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("你仍然可以指定一个版本,并在需要时重写 Spring Boot 的建议。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("p",[t._v("这个精心策划的列表包含你可以在 Spring 启动时使用的所有 Spring 模块,以及第三方库的完善列表。该清单是一份标准的材料清单("),r("code",[t._v("spring-boot-dependencies")]),t._v("),可用于"),r("a",{attrs:{href:"#using.build-systems.maven"}},[t._v("Maven")]),t._v("和"),r("a",{attrs:{href:"#using.build-systems.gradle"}},[t._v("Gradle")]),t._v("。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("Spring 启动的每个版本都与 Spring 框架的基本版本相关联。"),r("br"),t._v("我们"),r("strong",[t._v("高度")]),t._v("建议你不要指定其版本。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_1-2-maven"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_1-2-maven"}},[t._v("#")]),t._v(" 1.2. Maven")]),t._v(" "),r("p",[t._v("要了解如何在 Maven 中使用 Spring boot,请参阅 Spring boot 的 Maven 插件的文档:")]),t._v(" "),r("ul",[r("li",[r("p",[t._v("参考文献("),r("a",{attrs:{href:"https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/",target:"_blank",rel:"noopener noreferrer"}},[t._v("HTML"),r("OutboundLink")],1),t._v("和"),r("a",{attrs:{href:"https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/pdf/spring-boot-maven-plugin-reference.pdf",target:"_blank",rel:"noopener noreferrer"}},[t._v("PDF"),r("OutboundLink")],1),t._v(")")])]),t._v(" "),r("li",[r("p",[r("a",{attrs:{href:"https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/api/",target:"_blank",rel:"noopener noreferrer"}},[t._v("API"),r("OutboundLink")],1)])])]),t._v(" "),r("h3",{attrs:{id:"_1-3-gradle"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_1-3-gradle"}},[t._v("#")]),t._v(" 1.3. Gradle")]),t._v(" "),r("p",[t._v("要了解如何在 Gradle 中使用 Spring boot,请参阅 Spring boot 的 Gradle 插件的文档:")]),t._v(" "),r("ul",[r("li",[r("p",[t._v("参考文献("),r("a",{attrs:{href:"https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/",target:"_blank",rel:"noopener noreferrer"}},[t._v("HTML"),r("OutboundLink")],1),t._v("和"),r("a",{attrs:{href:"https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/pdf/spring-boot-gradle-plugin-reference.pdf",target:"_blank",rel:"noopener noreferrer"}},[t._v("PDF"),r("OutboundLink")],1),t._v(")")])]),t._v(" "),r("li",[r("p",[r("a",{attrs:{href:"https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/api/",target:"_blank",rel:"noopener noreferrer"}},[t._v("API"),r("OutboundLink")],1)])])]),t._v(" "),r("h3",{attrs:{id:"_1-4-ant"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_1-4-ant"}},[t._v("#")]),t._v(" 1.4. Ant")]),t._v(" "),r("p",[t._v("可以使用 Apache Ant +Ivy 构建 Spring 引导项目。"),r("code",[t._v("spring-boot-antlib")]),t._v("“antlib”模块也可用于帮助 Ant 创建可执行 JAR。")]),t._v(" "),r("p",[t._v("要声明依赖关系,典型的"),r("code",[t._v("ivy.xml")]),t._v("文件类似于以下示例:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('\n \n \n \n \n \n \n \n \n\n')])])]),r("p",[t._v("一个典型的"),r("code",[t._v("build.xml")]),t._v("看起来像以下示例:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('\n\n \n\n \n \n \n\n \n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n\n')])])]),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果你不想使用"),r("code",[t._v("spring-boot-antlib")]),t._v("模块,请参阅 "),r("em",[r("RouterLink",{attrs:{to:"/spring-boot/howto.html#howto.build.build-an-executable-archive-with-ant-without-using-spring-boot-antlib"}},[t._v("howto.html")])],1),t._v("“how-to”。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_1-5-初学者"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_1-5-初学者"}},[t._v("#")]),t._v(" 1.5.初学者")]),t._v(" "),r("p",[t._v("启动器是一组方便的依赖关系描述符,你可以将它们包含在应用程序中。你可以获得所需的所有 Spring 和相关技术的一站式服务,而无需搜索示例代码和复制粘贴依赖描述符的负载。例如,如果你希望开始使用 Spring 和 JPA 进行数据库访问,那么在你的项目中包含"),r("code",[t._v("spring-boot-starter-data-jpa")]),t._v("依赖项。")]),t._v(" "),r("p",[t._v("启动器包含许多依赖项,你需要这些依赖项才能使项目快速启动和运行,并具有一组一致的、受支持的托管传递依赖项。")]),t._v(" "),r("p",[t._v("名字里的含义是什么?")]),t._v(" "),r("p",[t._v("所有"),r("strong",[t._v("官员")]),t._v("启动器都遵循类似的命名模式;"),r("code",[t._v("spring-boot-starter-*")]),t._v(",其中"),r("code",[t._v("*")]),t._v("是一种特定类型的应用程序。此命名结构旨在帮助你在需要查找启动器时使用。 Maven 许多 IDE 中的集成允许你按名称搜索依赖项。例如,安装了适当的 Eclipse 或 Spring Tools 插件后,你可以在 POM 编辑器中按"),r("code",[t._v("ctrl-space")]),t._v("并输入“ Spring-boot-starter”以获得完整的列表。")]),t._v(" "),r("p",[t._v("正如在“"),r("RouterLink",{attrs:{to:"/spring-boot/features.html#features.developing-auto-configuration.custom-starter"}},[t._v("创建自己的启动器")]),t._v("”部分中所解释的,第三方启动器不应该以"),r("code",[t._v("spring-boot")]),t._v("开头,因为它是为官方 Spring 引导工件保留的。相反,第三方启动程序通常以项目的名称开始。例如,一个名为"),r("code",[t._v("thirdpartyproject")]),t._v("的第三方启动项目通常被命名为"),r("code",[t._v("thirdpartyproject-spring-boot-starter")]),t._v("。")],1),t._v(" "),r("p",[t._v("Spring Boot 在"),r("code",[t._v("org.springframework.boot")]),t._v("组下提供了以下应用程序启动器:")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th",[t._v("Name")]),t._v(" "),r("th",[t._v("说明")])])]),t._v(" "),r("tbody",[r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter")])]),t._v(" "),r("td",[t._v("核心启动器,包括自动配置支持、日志记录和 YAML")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-activemq")])]),t._v(" "),r("td",[t._v("使用 Apache ActiveMQ 进行 JMS 消息传递的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-amqp")])]),t._v(" "),r("td",[t._v("Spring AMQP 和兔 MQ 使用启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-aop")])]),t._v(" "),r("td",[t._v("Spring AOP 和 AspectJ 用于面向方面编程的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-artemis")])]),t._v(" "),r("td",[t._v("使用 Apache Artemis 进行 JMS 消息传递的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-batch")])]),t._v(" "),r("td",[t._v("用于使用 Spring 批的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-cache")])]),t._v(" "),r("td",[t._v("使用 Spring Framework 的缓存支持的入门工具")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-cassandra")])]),t._v(" "),r("td",[t._v("用于使用 Cassandra 分布式数据库的启动器和 Spring 数据 Cassandra")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-cassandra-reactive")])]),t._v(" "),r("td",[t._v("用于使用 Cassandra 分布式数据库的启动器和 Spring 数据 Cassandra 反应")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-couchbase")])]),t._v(" "),r("td",[t._v("用于使用 CouchBase 文档的面向数据库和 Spring 数据 CouchBase 的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-couchbase-reactive")])]),t._v(" "),r("td",[t._v("用于使用 CouchBase 面向文档的数据库和 Spring CouchBase 数据库的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-elasticsearch")])]),t._v(" "),r("td",[t._v("Spring 用于使用 ElasticSearch 搜索和分析引擎的启动器和数据 ElasticSearch")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-jdbc")])]),t._v(" "),r("td",[t._v("用于使用 Spring 数据 JDBC 的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-jpa")])]),t._v(" "),r("td",[t._v("用于使用 Spring 数据 JPA 和 Hibernate 的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-ldap")])]),t._v(" "),r("td",[t._v("用于使用 Spring 数据 LDAP 的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-mongodb")])]),t._v(" "),r("td",[t._v("用于使用 MongoDB 面向文档的数据库和 Spring 数据 MongoDB 的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-mongodb-reactive")])]),t._v(" "),r("td",[t._v("用于使用 MongoDB 面向文档的数据库和 Spring MongoDB 数据的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-neo4j")])]),t._v(" "),r("td",[t._v("用于使用 NEO 的启动器 4j 图数据库和 Spring 数据 NEO4j")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-r2dbc")])]),t._v(" "),r("td",[t._v("用于使用 Spring 数据 R2DBC 的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-redis")])]),t._v(" "),r("td",[t._v("用 Spring 数据 Redis 和生菜客户机使用 Redis 键值数据存储的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-redis-reactive")])]),t._v(" "),r("td",[t._v("用于使用 Redis 键值数据存储 Spring 数据的启动器 Redis Reactive 和 Lettuce 客户端")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-data-rest")])]),t._v(" "),r("td",[t._v("使用 Spring 数据 REST 在 REST 上公开 Spring 数据存储库的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-freemarker")])]),t._v(" "),r("td",[t._v("使用 Freemarker 视图构建 MVC Web 应用程序的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-groovy-templates")])]),t._v(" "),r("td",[t._v("使用 Groovy Templates 视图构建 MVC Web 应用程序的入门工具")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-hateoas")])]),t._v(" "),r("td",[t._v("用 Spring MVC 和 Spring Hateoas 构建基于超媒体的 RESTful Web 应用程序的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-integration")])]),t._v(" "),r("td",[t._v("用于使用 Spring 集成的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-jdbc")])]),t._v(" "),r("td",[t._v("使用 JDBC 和 HikarICP 连接池的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-jersey")])]),t._v(" "),r("td",[t._v("使用 JAX-RS 和 Jersey 构建 RESTful Web 应用程序的入门工具。["),r("code",[t._v("spring-boot-starter-web")]),t._v("](# Spring-boot-starter-web)的一种替代方法")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-jooq")])]),t._v(" "),r("td",[t._v("使用 Jooq 使用 JDBC 访问 SQL 数据库的启动器。["),r("code",[t._v("spring-boot-starter-data-jpa")]),t._v("](# Spring-boot-starter-data- JPA)或["),r("code",[t._v("spring-boot-starter-jdbc")]),t._v("](# Spring-boot-starter-jdbc)的替代方案)")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-json")])]),t._v(" "),r("td",[t._v("读写 JSON 的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-jta-atomikos")])]),t._v(" "),r("td",[t._v("使用 Atomikos 的 JTA 事务启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-mail")])]),t._v(" "),r("td",[t._v("使用 Java Mail 的启动器和 Spring Framework 的电子邮件发送支持")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-mustache")])]),t._v(" "),r("td",[t._v("使用 Mustache 视图构建 Web 应用程序的入门工具")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-oauth2-client")])]),t._v(" "),r("td",[t._v("使用 Spring Security 的 OAuth2/OpenID Connect 客户端功能的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-oauth2-resource-server")])]),t._v(" "),r("td",[t._v("使用 Spring Security 的 OAuth2Resource Server 特性的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-quartz")])]),t._v(" "),r("td",[t._v("使用 Quartz 调度器的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-rsocket")])]),t._v(" "),r("td",[t._v("用于构建 RSocket 客户机和服务器的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-security")])]),t._v(" "),r("td",[t._v("使用 Spring 安全性的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-test")])]),t._v(" "),r("td",[t._v("用包括 JUnit Jupiter、Hamcrest 和 Mockito 在内的库测试 Spring 引导应用程序的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-thymeleaf")])]),t._v(" "),r("td",[t._v("使用 ThymeLeaf 视图构建 MVC Web 应用程序的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-validation")])]),t._v(" "),r("td",[t._v("用 Hibernate 验证器使用 Java Bean 验证的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-web")])]),t._v(" "),r("td",[t._v("用于使用 Spring MVC 构建 Web(包括 RESTful)应用程序的启动器。使用 Tomcat 作为默认的嵌入式容器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-web-services")])]),t._v(" "),r("td",[t._v("用于使用 Spring Web 服务的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-webflux")])]),t._v(" "),r("td",[t._v("使用 Spring 框架的反应式 Web 支持构建 WebFlux 应用程序的启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-websocket")])]),t._v(" "),r("td",[t._v("用于使用 Spring 框架的 WebSocket 支持构建 WebSocket 应用程序的启动器")])])])]),t._v(" "),r("p",[t._v("除了应用程序启动器之外,还可以使用以下启动器添加 "),r("em",[r("RouterLink",{attrs:{to:"/spring-boot/actuator.html#actuator"}},[t._v("生产准备就绪")])],1),t._v(" 特性:")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th",[t._v("Name")]),t._v(" "),r("th",[t._v("说明")])])]),t._v(" "),r("tbody",[r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-actuator")])]),t._v(" "),r("td",[t._v("使用 Spring Boot’s Actuator 的启动器,它提供了生产就绪功能,可帮助你监视和管理你的应用程序")])])])]),t._v(" "),r("p",[t._v("最后, Spring 引导还包括以下启动器,如果你想要排除或交换特定的技术方面,可以使用这些启动器:")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th",[t._v("Name")]),t._v(" "),r("th",[t._v("说明")])])]),t._v(" "),r("tbody",[r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-jetty")])]),t._v(" "),r("td",[t._v("用于使用 Jetty 作为嵌入式 Servlet 容器的启动器。["),r("code",[t._v("spring-boot-starter-tomcat")]),t._v("]的替代方法(# Spring-boot-starter- Tomcat)")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-log4j2")])]),t._v(" "),r("td",[t._v("使用 log4j2 进行日志记录的启动器。["),r("code",[t._v("spring-boot-starter-logging")]),t._v("](# Spring-boot-starter-logging)的一种替代方法)")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-logging")])]),t._v(" "),r("td",[t._v("使用日志记录的启动器。默认日志启动器")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-reactor-netty")])]),t._v(" "),r("td",[t._v("使用反应器网络作为嵌入式反应性 HTTP 服务器的启动器。")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-tomcat")])]),t._v(" "),r("td",[t._v("用于使用 Tomcat 作为嵌入式 Servlet 容器的启动器。["),r("code",[t._v("spring-boot-starter-web")]),t._v("]使用的默认 Servlet 容器启动器(# Spring-boot-starter-web)")])]),t._v(" "),r("tr",[r("td",[r("a",{attrs:{href:""}}),r("code",[t._v("spring-boot-starter-undertow")])]),t._v(" "),r("td",[t._v("用于使用 Undertow 作为嵌入式 Servlet 容器的启动器。["),r("code",[t._v("spring-boot-starter-tomcat")]),t._v("](# Spring-boot-starter- Tomcat)的替代方案)")])])])]),t._v(" "),r("p",[t._v("要了解如何交换技术方面,请参阅"),r("RouterLink",{attrs:{to:"/spring-boot/howto.html#howto.webserver.use-another"}},[t._v("交换 Web 服务器")]),t._v("和"),r("RouterLink",{attrs:{to:"/spring-boot/howto.html#howto.logging.log4j"}},[t._v("测井系统")]),t._v("的操作文档。")],1),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("有关其他社区贡献的启动器的列表,请参见 Github 上"),r("code",[t._v("spring-boot-starters")]),t._v("模块中的"),r("a",{attrs:{href:"https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-starters/README.adoc",target:"_blank",rel:"noopener noreferrer"}},[t._v("自述文件"),r("OutboundLink")],1),t._v("。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h2",{attrs:{id:"_2-构建你的代码"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_2-构建你的代码"}},[t._v("#")]),t._v(" 2. 构建你的代码")]),t._v(" "),r("p",[t._v("Spring 启动不需要任何特定的代码布局来工作。然而,有一些最佳实践是有帮助的。")]),t._v(" "),r("h3",{attrs:{id:"_2-1-使用-默认-包"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_2-1-使用-默认-包"}},[t._v("#")]),t._v(" 2.1.使用“默认”包")]),t._v(" "),r("p",[t._v("当一个类不包含"),r("code",[t._v("package")]),t._v("声明时,它被认为是在“缺省包”中。“默认包”的使用通常是不鼓励的,应该避免。对于使用"),r("code",[t._v("@ComponentScan")]),t._v("、"),r("code",[t._v("@Configuration属性Scan")]),t._v("、"),r("code",[t._v("@EntityScan")]),t._v("或"),r("code",[t._v("@SpringBootApplication")]),t._v("注释的 Spring 引导应用程序来说,这可能会导致特殊的问题,因为每个 jar 中的每个类都被读取。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("我们建议你遵循 Java 推荐的包命名约定,并使用一个反向域名(例如,"),r("code",[t._v("com.example.project")]),t._v(")。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_2-2-定位主应用程序类"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_2-2-定位主应用程序类"}},[t._v("#")]),t._v(" 2.2.定位主应用程序类")]),t._v(" "),r("p",[t._v("我们通常建议你将主应用程序类定位在一个根包中,而不是其他类。["),r("code",[t._v("@SpringBootApplication")]),t._v("注释](#using.using-the-springbootapplication-annotation)通常放在主类上,它隐式地为某些项定义了一个基本的“搜索包”。例如,如果你正在编写 JPA 应用程序,则使用"),r("code",[t._v("@SpringBootApplication")]),t._v("注释类的包来搜索"),r("code",[t._v("@Entity")]),t._v("项。使用根包还允许组件扫描仅应用于你的项目。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果不想使用"),r("code",[t._v("@SpringBootApplication")]),t._v(",则它导入的"),r("code",[t._v("@EnableAutoConfiguration")]),t._v("和"),r("code",[t._v("@ComponentScan")]),t._v("注释定义了该行为,因此你也可以使用这些注释。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("p",[t._v("下面的清单显示了一个典型的布局:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("com\n +- example\n +- myapplication\n +- MyApplication.java\n |\n +- customer\n | +- Customer.java\n | +- CustomerController.java\n | +- CustomerService.java\n | +- CustomerRepository.java\n |\n +- order\n +- Order.java\n +- OrderController.java\n +- OrderService.java\n +- OrderRepository.java\n")])])]),r("p",[r("code",[t._v("MyApplication.java")]),t._v("文件将声明"),r("code",[t._v("main")]),t._v("方法,以及基本的"),r("code",[t._v("@SpringBootApplication")]),t._v(",如下所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("import org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MyApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(MyApplication.class, args);\n }\n\n}\n\n")])])]),r("h2",{attrs:{id:"_3-配置类"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_3-配置类"}},[t._v("#")]),t._v(" 3. 配置类")]),t._v(" "),r("p",[t._v("Spring 引导有利于基于 Java 的配置。尽管在 XML 源中可以使用"),r("code",[t._v("SpringApplication")]),t._v(",但我们通常建议你的主要源是一个单独的"),r("code",[t._v("@Configuration")]),t._v("类。通常,定义"),r("code",[t._v("main")]),t._v("方法的类是作为主要"),r("code",[t._v("@Configuration")]),t._v("方法的很好的候选者。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("Spring 互联网上已经发布了许多使用 XML 配置的配置示例。"),r("br"),t._v("如果可能的话,总是尝试使用等效的基于 Java 的配置。"),r("br"),t._v("搜索"),r("code",[t._v("Enable*")]),t._v("注释可以是一个很好的起点。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_3-1-导入额外的配置类"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_3-1-导入额外的配置类"}},[t._v("#")]),t._v(" 3.1.导入额外的配置类")]),t._v(" "),r("p",[t._v("你不需要将所有的"),r("code",[t._v("@Configuration")]),t._v("都放入一个类中。"),r("code",[t._v("@Import")]),t._v("注释可用于导入其他配置类。或者,你可以使用"),r("code",[t._v("@ComponentScan")]),t._v("来自动拾取所有 Spring 组件,包括"),r("code",[t._v("@Configuration")]),t._v("类。")]),t._v(" "),r("h3",{attrs:{id:"_3-2-导入-xml-配置"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_3-2-导入-xml-配置"}},[t._v("#")]),t._v(" 3.2.导入 XML 配置")]),t._v(" "),r("p",[t._v("如果你绝对必须使用基于 XML 的配置,我们建议你仍然从"),r("code",[t._v("@Configuration")]),t._v("类开始。然后可以使用"),r("code",[t._v("@ImportResource")]),t._v("注释来加载 XML 配置文件。")]),t._v(" "),r("h2",{attrs:{id:"_4-自动配置"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_4-自动配置"}},[t._v("#")]),t._v(" 4. 自动配置")]),t._v(" "),r("p",[t._v("Spring 引导自动配置尝试基于你已添加的 jar 依赖项自动配置你的 Spring 应用程序。例如,如果"),r("code",[t._v("HSQLDB")]),t._v("在你的 Classpath 上,并且你还没有手动配置任何数据库连接 bean,那么 Spring 引导自动配置内存中的数据库。")]),t._v(" "),r("p",[t._v("你需要通过将"),r("code",[t._v("@EnableAutoConfiguration")]),t._v("或"),r("code",[t._v("@SpringBootApplication")]),t._v("注释添加到你的"),r("code",[t._v("@Configuration")]),t._v("类中来 OPT 到自动配置中。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("你应该只添加一个"),r("code",[t._v("@SpringBootApplication")]),t._v("或"),r("code",[t._v("@EnableAutoConfiguration")]),t._v("注释。"),r("br"),t._v("我们通常建议你只在主"),r("code",[t._v("@Configuration")]),t._v("类中添加一个或另一个。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_4-1-逐步取代自动配置"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_4-1-逐步取代自动配置"}},[t._v("#")]),t._v(" 4.1.逐步取代自动配置")]),t._v(" "),r("p",[t._v("自动配置是非侵入性的。在任何时候,你都可以开始定义自己的配置,以替换自动配置的特定部分。例如,如果你添加了自己的"),r("code",[t._v("DataSource")]),t._v(" Bean,那么默认的嵌入式数据库支持就退后了。")]),t._v(" "),r("p",[t._v("如果需要找出当前正在应用的自动配置,以及为什么,请使用"),r("code",[t._v("--debug")]),t._v("开关启动应用程序。这样做可以为选择的核心记录器启用调试日志,并将一个条件报告给控制台。")]),t._v(" "),r("h3",{attrs:{id:"_4-2-禁用特定的自动配置类"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_4-2-禁用特定的自动配置类"}},[t._v("#")]),t._v(" 4.2.禁用特定的自动配置类")]),t._v(" "),r("p",[t._v("如果你发现你不想要的特定自动配置类正在被应用,那么你可以使用"),r("code",[t._v("@SpringBootApplication")]),t._v("的 exclude 属性来禁用它们,如下面的示例所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("import org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\n\n@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })\npublic class MyApplication {\n\n}\n\n")])])]),r("p",[t._v("如果类不在 Classpath 上,则可以使用注释的"),r("code",[t._v("excludeName")]),t._v("属性并指定完全限定的名称。如果你更喜欢使用"),r("code",[t._v("@EnableAutoConfiguration")]),t._v("而不是"),r("code",[t._v("@SpringBootApplication")]),t._v(",也可以使用"),r("code",[t._v("exclude")]),t._v("和"),r("code",[t._v("excludeName")]),t._v("。最后,你还可以使用"),r("code",[t._v("spring.autoconfigure.exclude")]),t._v("属性来控制要排除的自动配置类列表。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("你可以在注释级别和通过使用属性来定义排除。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("尽管自动配置类是"),r("code",[t._v("public")]),t._v(",但该类中唯一被认为是公共 API 的方面是可用于禁用自动配置的类的名称,"),r("br"),t._v("这些类的实际内容,例如嵌套配置类或 Bean 方法仅供内部使用,我们不建议直接使用这些方法。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h2",{attrs:{id:"_5-spring-bean-和依赖注入"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_5-spring-bean-和依赖注入"}},[t._v("#")]),t._v(" 5. Spring bean 和依赖注入")]),t._v(" "),r("p",[t._v("你可以自由地使用任何标准的 Spring 框架技术来定义你的 bean 及其注入的依赖项。我们通常建议使用构造函数注入来连接依赖项,并使用"),r("code",[t._v("@ComponentScan")]),t._v("来查找 bean。")]),t._v(" "),r("p",[t._v("如果按照上面的建议对代码进行结构设计(将应用程序类定位在顶级包中),则可以添加"),r("code",[t._v("@ComponentScan")]),t._v("而不带任何参数,或者使用"),r("code",[t._v("@SpringBootApplication")]),t._v("注释,隐式地包含它。你的所有应用程序组件("),r("code",[t._v("@Component")]),t._v(","),r("code",[t._v("@Service")]),t._v(","),r("code",[t._v("@Repository")]),t._v(","),r("code",[t._v("@Controller")]),t._v(",以及其他)都会自动注册为 Spring bean。")]),t._v(" "),r("p",[t._v("下面的示例显示了一个"),r("code",[t._v("@Service")]),t._v(" Bean,它使用构造函数注入来获得所需的"),r("code",[t._v("RiskAssessor")]),t._v(" Bean:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("import org.springframework.stereotype.Service;\n\n@Service\npublic class MyAccountService implements AccountService {\n\n private final RiskAssessor riskAssessor;\n\n public MyAccountService(RiskAssessor riskAssessor) {\n this.riskAssessor = riskAssessor;\n }\n\n // ...\n\n}\n\n")])])]),r("p",[t._v("如果 Bean 具有多个构造函数,则需要将希望 Spring 使用的构造函数标记为"),r("code",[t._v("@Autowired")]),t._v(":")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("import java.io.PrintStream;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class MyAccountService implements AccountService {\n\n private final RiskAssessor riskAssessor;\n\n private final PrintStream out;\n\n @Autowired\n public MyAccountService(RiskAssessor riskAssessor) {\n this.riskAssessor = riskAssessor;\n this.out = System.out;\n }\n\n public MyAccountService(RiskAssessor riskAssessor, PrintStream out) {\n this.riskAssessor = riskAssessor;\n this.out = out;\n }\n\n // ...\n\n}\n\n")])])]),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("请注意,如何使用构造函数注入使"),r("code",[t._v("riskAssessor")]),t._v("字段标记为"),r("code",[t._v("final")]),t._v(",这表明它不能随后进行更改。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h2",{attrs:{id:"_6-使用-springbootapplication-注释"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_6-使用-springbootapplication-注释"}},[t._v("#")]),t._v(" 6. 使用 @SpringBootApplication 注释")]),t._v(" "),r("p",[t._v("Spring 许多引导开发人员喜欢他们的应用程序使用自动配置、组件扫描,并能够在他们的“应用程序类”上定义额外的配置。可以使用一个"),r("code",[t._v("@SpringBootApplication")]),t._v("注释来启用这三个特性,即:")]),t._v(" "),r("ul",[r("li",[r("p",[r("code",[t._v("@EnableAutoConfiguration")]),t._v(":启用"),r("a",{attrs:{href:"#using.auto-configuration"}},[t._v("Spring Boot’s auto-configuration mechanism")])])]),t._v(" "),r("li",[r("p",[r("code",[t._v("@ComponentScan")]),t._v(":在应用程序所在的包上启用"),r("code",[t._v("@Component")]),t._v("扫描(参见"),r("a",{attrs:{href:"#using.structuring-your-code"}},[t._v("最佳实践")]),t._v(")")])]),t._v(" "),r("li",[r("p",[r("code",[t._v("@SpringBootConfiguration")]),t._v(":允许在上下文中注册额外的 bean 或导入额外的配置类。 Spring 的标准"),r("code",[t._v("@Configuration")]),t._v("的一种替代方法,在集成测试中辅助"),r("RouterLink",{attrs:{to:"/spring-boot/features.html#features.testing.spring-boot-applications.detecting-configuration"}},[t._v("配置检测")]),t._v("。")],1)])]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("import org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n// Same as @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan\n@SpringBootApplication\npublic class MyApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(MyApplication.class, args);\n }\n\n}\n\n")])])]),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[r("code",[t._v("@SpringBootApplication")]),t._v("还提供别名来定制"),r("code",[t._v("@EnableAutoConfiguration")]),t._v("和"),r("code",[t._v("@ComponentScan")]),t._v("的属性。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("这些特性都不是强制性的,你可以选择用它启用的任何特性来替换这个注释。"),r("br"),t._v("例如,你可能不希望在应用程序中使用组件扫描或配置属性扫描:"),r("br"),r("br"),r("code",[t._v("
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Import;

@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
@Import({ SomeConfiguration.class, AnotherConfiguration.class })
public class MyApplication {

public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

}

")]),r("br"),t._v("在此示例中,"),r("code",[t._v("MyApplication")]),t._v("就像任何其他 Spring 启动应用程序一样,除了"),r("code",[t._v("@Component")]),t._v("-带注释的类和"),r("code",[t._v("@Configuration属性")]),t._v("-带注释的类不会自动检测到,并且显式地导入了用户定义的 bean(参见"),r("code",[t._v("@Import")]),t._v(")。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h2",{attrs:{id:"_7-运行你的应用程序"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_7-运行你的应用程序"}},[t._v("#")]),t._v(" 7. 运行你的应用程序")]),t._v(" "),r("p",[t._v("将应用程序打包为 jar 并使用嵌入式 HTTP 服务器的最大优势之一是,你可以像运行其他应用程序一样运行你的应用程序。该示例应用于调试 Spring 引导应用程序。你不需要任何特殊的 IDE 插件或扩展。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("本节只讨论基于 jar 的打包。"),r("br"),t._v("如果你选择将应用程序打包为 WAR 文件,请参阅你的服务器和 IDE 文档。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_7-1-从-ide-中运行"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_7-1-从-ide-中运行"}},[t._v("#")]),t._v(" 7.1.从 IDE 中运行")]),t._v(" "),r("p",[t._v("你可以将 IDE 中的 Spring 引导应用程序作为 Java 应用程序运行。然而,你首先需要导入你的项目。导入步骤根据你的 IDE 和构建系统而有所不同。大多数 IDE 可以直接导入 Maven 项目。例如,Eclipse 用户可以从"),r("code",[t._v("File")]),t._v("菜单中选择"),r("code",[t._v("Import…​``Existing Maven Projects")]),t._v("。")]),t._v(" "),r("p",[t._v("如果不能直接将项目导入 IDE,则可以使用构建插件生成 IDE 元数据。 Maven 包括用于"),r("a",{attrs:{href:"https://maven.apache.org/plugins/maven-eclipse-plugin/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Eclipse"),r("OutboundLink")],1),t._v("和"),r("a",{attrs:{href:"https://maven.apache.org/plugins/maven-idea-plugin/",target:"_blank",rel:"noopener noreferrer"}},[t._v("IDEA"),r("OutboundLink")],1),t._v("的插件。 Gradle 为"),r("a",{attrs:{href:"https://docs.gradle.org/current/userguide/userguide.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("各种 IDE"),r("OutboundLink")],1),t._v("提供插件。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果你不小心运行了一个 Web 应用程序两次,你会看到一个“Port Already in Use”错误。"),r("br"),t._v(" Spring Tools 用户可以使用"),r("code",[t._v("Relaunch")]),t._v("按钮,而不是"),r("code",[t._v("Run")]),t._v("按钮,以确保任何现有实例都已关闭。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_7-2-以打包应用程序的形式运行"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_7-2-以打包应用程序的形式运行"}},[t._v("#")]),t._v(" 7.2.以打包应用程序的形式运行")]),t._v(" "),r("p",[t._v("如果使用 Spring 引导 Maven 或 Gradle 插件创建可执行文件 jar,则可以使用"),r("code",[t._v("java -jar")]),t._v("运行应用程序,如以下示例所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar\n")])])]),r("p",[t._v("还可以在启用远程调试支持的情况下运行打包应用程序。这样做可以将一个调试器附加到打包的应用程序中,如以下示例所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \\\n -jar target/myapplication-0.0.1-SNAPSHOT.jar\n")])])]),r("h3",{attrs:{id:"_7-3-使用-maven-插件"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_7-3-使用-maven-插件"}},[t._v("#")]),t._v(" 7.3.使用 Maven 插件")]),t._v(" "),r("p",[t._v("Spring 引导 Maven 插件包括一个"),r("code",[t._v("run")]),t._v("目标,该目标可用于快速编译和运行你的应用程序。应用程序以分块的形式运行,就像它们在你的 IDE 中一样。下面的示例显示了用于运行 Spring 引导应用程序的典型 Maven 命令:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("$ mvn spring-boot:run\n")])])]),r("p",[t._v("你可能还希望使用"),r("code",[t._v("MAVEN_OPTS")]),t._v("操作系统环境变量,如以下示例所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("$ export MAVEN_OPTS=-Xmx1024m\n")])])]),r("h3",{attrs:{id:"_7-4-使用-gradle-插件"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_7-4-使用-gradle-插件"}},[t._v("#")]),t._v(" 7.4.使用 Gradle 插件")]),t._v(" "),r("p",[t._v("Spring boot Gradle 插件还包括一个"),r("code",[t._v("bootRun")]),t._v("任务,该任务可用于以扩展形式运行你的应用程序。每当你应用"),r("code",[t._v("org.springframework.boot")]),t._v("和"),r("code",[t._v("java")]),t._v("插件时,都会添加"),r("code",[t._v("bootRun")]),t._v("任务,如下例所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("$ gradle bootRun\n")])])]),r("p",[t._v("你可能还希望使用"),r("code",[t._v("JAVA_OPTS")]),t._v("操作系统环境变量,如以下示例所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("$ export JAVA_OPTS=-Xmx1024m\n")])])]),r("h3",{attrs:{id:"_7-5-热交换"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_7-5-热交换"}},[t._v("#")]),t._v(" 7.5.热交换")]),t._v(" "),r("p",[t._v("Spring 由于引导应用程序是普通的 Java 应用程序,所以 JVM 热交换应该是开箱即用的。JVM 热交换在一定程度上受到它可以替换的字节码的限制。对于更完整的解决方案,可以使用"),r("a",{attrs:{href:"https://www.jrebel.com/products/jrebel",target:"_blank",rel:"noopener noreferrer"}},[t._v("JRebel"),r("OutboundLink")],1),t._v("。")]),t._v(" "),r("p",[r("code",[t._v("spring-boot-devtools")]),t._v("模块还包括对快速应用程序重启的支持。有关详细信息,请参见"),r("RouterLink",{attrs:{to:"/spring-boot/howto.html#howto.hotswapping"}},[t._v("热点交换“操作”")]),t._v("。")],1),t._v(" "),r("h2",{attrs:{id:"_8-开发工具"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-开发工具"}},[t._v("#")]),t._v(" 8. 开发工具")]),t._v(" "),r("p",[t._v("Spring 启动包括一组额外的工具,这些工具可以使应用程序的开发体验稍微更愉快一些。"),r("code",[t._v("spring-boot-devtools")]),t._v("模块可以包含在任何项目中,以提供额外的开发时功能。要包含 DevTools 支持,请将模块依赖项添加到你的构建中,如以下 Maven 和 Gradle 的清单所示:")]),t._v(" "),r("p",[t._v("Maven")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("\n \n org.springframework.boot\n spring-boot-devtools\n true\n \n\n")])])]),r("p",[t._v("Gradle")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('dependencies {\n developmentOnly("org.springframework.boot:spring-boot-devtools")\n}\n')])])]),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("DevTools 可能会导致类加载问题,特别是在多模块项目中。"),r("a",{attrs:{href:"#using.devtools.diagnosing-classloading-issues"}},[t._v("诊断类加载问题")]),t._v("解释了如何诊断和解决它们。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("在运行完全打包的应用程序时,开发人员工具会自动禁用。"),r("br"),t._v("如果你的应用程序是从"),r("code",[t._v("java -jar")]),t._v("启动的,或者是从一个特殊的类加载器启动的,然后,它被认为是一个“生产应用程序”,"),r("br"),t._v("你可以通过使用"),r("code",[t._v("spring.devtools.restart.enabled")]),t._v("系统属性来控制这种行为,"),r("br"),t._v("可以启用 DevTools,而不考虑启动应用程序所使用的类装入器,设置"),r("code",[t._v("-Dspring.devtools.restart.enabled=true")]),t._v("系统属性。"),r("br"),t._v("在运行 DevTools 存在安全风险的生产环境中,不能这样做。"),r("br"),t._v("要禁用 DevTools,排除依赖关系或设置"),r("code",[t._v("-Dspring.devtools.restart.enabled=false")]),t._v("系统属性。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("在 Maven 中将依赖标记为可选的,或者在 Gradle 中使用"),r("code",[t._v("developmentOnly")]),t._v("配置(如上图所示),可以防止将 DevTools 传递地应用于使用你的项目的其他模块。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("重新打包的归档文件默认情况下不包含 DevTools。"),r("br"),t._v("如果你想使用"),r("a",{attrs:{href:"#using.devtools.remote-applications"}},[t._v("某些远程 DevTools 功能")]),t._v(",则需要包含它。"),r("br"),t._v("在使用 Maven 插件时,将"),r("code",[t._v("excludeDevtools")]),t._v("属性设置为"),r("code",[t._v("false")]),t._v("。在使用 Gradle 插件时,"),r("br"),t._v(",[将任务的 Classpath 配置为包括"),r("code",[t._v("developmentOnly")]),t._v("配置](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/ Gradle-plugin/reference/htmlsingle/#packaging-executable-configuring-inuiting-including-development-only-dependencies)。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_8-1-诊断类加载问题"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-1-诊断类加载问题"}},[t._v("#")]),t._v(" 8.1.诊断类加载问题")]),t._v(" "),r("p",[t._v("如"),r("a",{attrs:{href:"#using.devtools.restart.restart-vs-reload"}},[t._v("重新启动 VS 重新加载")]),t._v("小节中所述,重新启动功能是通过使用两个类装入器来实现的。对于大多数应用程序来说,这种方法效果很好。然而,它有时会导致类加载问题,特别是在多模块项目中。")]),t._v(" "),r("p",[t._v("要诊断类加载问题是否确实是由 DevTools 及其两个类加载器引起的,"),r("a",{attrs:{href:"#using.devtools.restart.disable"}},[t._v("尝试禁用重新启动")]),t._v("。如果这解决了你的问题,"),r("a",{attrs:{href:"#using.devtools.restart.customizing-the-classload"}},[t._v("自定义重新启动类装入器")]),t._v("将包含你的整个项目。")]),t._v(" "),r("h3",{attrs:{id:"_8-2-属性默认值"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-2-属性默认值"}},[t._v("#")]),t._v(" 8.2.属性默认值")]),t._v(" "),r("p",[t._v("Spring 引导支持的几个库使用缓存来提高性能。例如,"),r("RouterLink",{attrs:{to:"/spring-boot/web.html#web.servlet.spring-mvc.template-engines"}},[t._v("模板引擎")]),t._v("缓存已编译的模板,以避免重复解析模板文件。另外, Spring MVC 可以在提供静态资源时将 HTTP 缓存头添加到响应中。")],1),t._v(" "),r("p",[t._v("虽然缓存在生产中非常有益,但在开发过程中可能会适得其反,从而使你无法看到刚刚在应用程序中所做的更改。出于这个原因, Spring-boot-devtools 默认禁用缓存选项。")]),t._v(" "),r("p",[t._v("缓存选项通常由"),r("code",[t._v("application.properties")]),t._v("文件中的设置来配置。例如,ThymeLeaf 提供"),r("code",[t._v("spring.thymeleaf.cache")]),t._v("属性。不需要手动设置这些属性,"),r("code",[t._v("spring-boot-devtools")]),t._v("模块会自动应用合理的开发时配置。")]),t._v(" "),r("p",[t._v("由于在开发 Spring MVC 和 Spring WebFlux 应用程序时需要有关 Web 请求的更多信息,因此 Developer Tools 建议你为"),r("code",[t._v("DEBUG")]),t._v("日志记录组启用"),r("code",[t._v("web")]),t._v("日志记录。这将为你提供有关传入请求的信息、处理该请求的处理程序、响应结果以及其他详细信息。如果希望记录所有请求细节(包括可能敏感的信息),可以打开"),r("code",[t._v("spring.mvc.log-request-details")]),t._v("或"),r("code",[t._v("spring.codec.log-request-details")]),t._v("配置属性。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果不希望应用属性默认值,可以在"),r("code",[t._v("application.properties")]),t._v("中将"),r("code",[t._v("spring.devtools.add-properties")]),t._v("设置为"),r("code",[t._v("false")]),t._v("。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("有关 DevTools 应用的属性的完整列表,请参见"),r("a",{attrs:{href:"https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java",target:"_blank",rel:"noopener noreferrer"}},[t._v("DevToolsPropertyDefaultSpostProcessor"),r("OutboundLink")],1),t._v("。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_8-3-自动重启"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-自动重启"}},[t._v("#")]),t._v(" 8.3.自动重启")]),t._v(" "),r("p",[t._v("每当 Classpath 上的文件发生更改时,使用"),r("code",[t._v("spring-boot-devtools")]),t._v("的应用程序会自动重新启动。当在 IDE 中工作时,这可能是一个有用的特性,因为它为代码更改提供了一个非常快的反馈循环。默认情况下, Classpath 上指向某个目录的任何条目都会被监视以进行更改。请注意,某些资源,例如静态资产和视图模板,"),r("a",{attrs:{href:"#using.devtools.restart.excluding-resources"}},[t._v("不需要重新启动应用程序")]),t._v("。")]),t._v(" "),r("p",[t._v("触发重新启动")]),t._v(" "),r("p",[t._v("由于 DevTools 监视 Classpath 资源,触发重启的唯一方法是更新 Classpath。使 Classpath 更新的方式取决于你使用的 IDE:")]),t._v(" "),r("ul",[r("li",[r("p",[t._v("在 Eclipse 中,保存修改后的文件会导致 Classpath 被更新并触发重新启动。")])]),t._v(" "),r("li",[r("p",[t._v("在 IntelliJ IDEA 中,构建项目("),r("code",[t._v("Build +→+ Build Project")]),t._v(")具有相同的效果。")])]),t._v(" "),r("li",[r("p",[t._v("如果使用构建插件,对 Maven 运行"),r("code",[t._v("mvn compile")]),t._v("或对 Gradle 运行"),r("code",[t._v("gradle build")]),t._v("将触发重新启动。")])])]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果使用 Maven 或 Gradle 使用构建插件重新启动,则必须将"),r("code",[t._v("forking")]),t._v("设置为"),r("code",[t._v("enabled")]),t._v("。"),r("br"),t._v("如果禁用分叉,将不会创建 DevTools 使用的隔离应用程序类装入器,并且重新启动将无法正常运行。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("当与 LiveReload 一起使用时,自动重启非常有效。"),r("a",{attrs:{href:"#using.devtools.livereload"}},[t._v("参见 LiveReload 部分")]),t._v("有关详细信息。"),r("br"),t._v("如果使用 JRebel,自动重启将被禁用,以利于动态类重新加载。"),r("br"),t._v("其他 DevTools 功能(例如 LiveReload 和 Property overrides)仍然可以使用。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("DevTools 依赖应用程序上下文的关机钩子在重新启动时关闭它。"),r("br"),t._v("如果禁用了关机钩子("),r("code",[t._v("SpringApplication.setRegisterShutdownHook(false)")]),t._v("),它将无法正常工作。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("DevTools 需要自定义"),r("code",[t._v("ApplicationContext")]),t._v("所使用的"),r("code",[t._v("ResourceLoader")]),t._v("。"),r("br"),t._v("如果你的应用程序已经提供了一个,它将被包装。"),r("br"),t._v("不支持"),r("code",[t._v("getResource")]),t._v("方法上的"),r("code",[t._v("getResource")]),t._v("直接覆盖。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("使用 AspectJ 编织时不支持自动重新启动。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("p",[t._v("重新启动 VS 重新加载")]),t._v(" "),r("p",[t._v("Spring Boot 提供的重启技术通过使用两个类装入器来工作。不会更改的类(例如,来自第三方 JAR 的类)被加载到"),r("em",[t._v("基座")]),t._v("类装入器中。你正在积极开发的类被加载到"),r("em",[t._v("重新启动")]),t._v("类装入器中。当重新启动应用程序时,将丢弃"),r("em",[t._v("重新启动")]),t._v("类装入器,并创建一个新的类装入器。这种方法意味着应用程序的重新启动通常比“冷启动”快得多,因为"),r("em",[t._v("基座")]),t._v("类装入器已经可用并填充了。")]),t._v(" "),r("p",[t._v("如果你发现应用程序的重新启动速度不够快,或者遇到类加载问题,那么可以考虑重新加载 ZeroTurnaround 中的"),r("a",{attrs:{href:"https://jrebel.com/software/jrebel/",target:"_blank",rel:"noopener noreferrer"}},[t._v("JRebel"),r("OutboundLink")],1),t._v("等技术。这些工作是在加载时重写类,以使它们更易于重新加载。")]),t._v(" "),r("h4",{attrs:{id:"_8-3-1-状态评估中的测井变化"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-1-状态评估中的测井变化"}},[t._v("#")]),t._v(" 8.3.1.状态评估中的测井变化")]),t._v(" "),r("p",[t._v("默认情况下,每次应用程序重新启动时,都会记录一份显示条件评估增量的报告。当你进行添加或删除 bean 和设置配置属性等更改时,该报告将显示对应用程序自动配置的更改。")]),t._v(" "),r("p",[t._v("要禁用报告的日志记录,请设置以下属性:")]),t._v(" "),r("p",[t._v("属性")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("spring.devtools.restart.log-condition-evaluation-delta=false\n")])])]),r("p",[t._v("Yaml")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("spring:\n devtools:\n restart:\n log-condition-evaluation-delta: false\n")])])]),r("h4",{attrs:{id:"_8-3-2-不包括资源"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-2-不包括资源"}},[t._v("#")]),t._v(" 8.3.2.不包括资源")]),t._v(" "),r("p",[t._v("某些资源在被更改时不一定需要触发重新启动。例如,胸腺叶模板可以就地编辑。默认情况下,在"),r("code",[t._v("/META-INF/maven")]),t._v("、"),r("code",[t._v("/META-INF/resources")]),t._v("、"),r("code",[t._v("/resources")]),t._v("、"),r("code",[t._v("/static")]),t._v("、"),r("code",[t._v("/public")]),t._v("或"),r("code",[t._v("/templates")]),t._v("中更改资源不会触发重启,但会触发"),r("a",{attrs:{href:"#using.devtools.livereload"}},[t._v("实时重新加载")]),t._v("。如果希望自定义这些排除,可以使用"),r("code",[t._v("spring.devtools.restart.exclude")]),t._v("属性。例如,要仅排除"),r("code",[t._v("/static")]),t._v("和"),r("code",[t._v("/public")]),t._v(",你需要设置以下属性:")]),t._v(" "),r("p",[t._v("属性")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("spring.devtools.restart.exclude=static/**,public/**\n")])])]),r("p",[t._v("Yaml")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('spring:\n devtools:\n restart:\n exclude: "static/**,public/**"\n')])])]),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果你希望保留这些默认值和"),r("em",[t._v("添加")]),t._v("附加排除项,请使用"),r("code",[t._v("spring.devtools.restart.additional-exclude")]),t._v("属性。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h4",{attrs:{id:"_8-3-3-观看其他路径"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-3-观看其他路径"}},[t._v("#")]),t._v(" 8.3.3.观看其他路径")]),t._v(" "),r("p",[t._v("当你对不在 Classpath 上的文件进行更改时,你可能希望你的应用程序被重新启动或重新加载。要做到这一点,请使用"),r("code",[t._v("spring.devtools.restart.additional-paths")]),t._v("属性来配置其他路径,以监视更改。你可以使用"),r("code",[t._v("spring.devtools.restart.exclude")]),t._v("属性"),r("a",{attrs:{href:"#using.devtools.restart.excluding-resources"}},[t._v("前面描述的")]),t._v("来控制附加路径下方的更改是否触发完全重新启动或"),r("a",{attrs:{href:"#using.devtools.livereload"}},[t._v("实时重新加载")]),t._v("。")]),t._v(" "),r("h4",{attrs:{id:"_8-3-4-禁用重新启动"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-4-禁用重新启动"}},[t._v("#")]),t._v(" 8.3.4.禁用重新启动")]),t._v(" "),r("p",[t._v("如果不想使用 Restart 特性,可以使用"),r("code",[t._v("spring.devtools.restart.enabled")]),t._v("属性禁用它。在大多数情况下,你可以在"),r("code",[t._v("application.properties")]),t._v("中设置此属性(这样做仍然会初始化 Restart 类装入器,但它不会监视文件更改)。")]),t._v(" "),r("p",[t._v("如果需要"),r("em",[t._v("完全")]),t._v("禁用重启支持(例如,因为它不能与特定的库一起工作),则需要在调用"),r("code",[t._v("spring.devtools.restart.enabled``System")]),t._v("之前将"),r("code",[t._v("false")]),t._v("属性设置为"),r("code",[t._v("false")]),t._v(",如下面的示例所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('import org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MyApplication {\n\n public static void main(String[] args) {\n System.setProperty("spring.devtools.restart.enabled", "false");\n SpringApplication.run(MyApplication.class, args);\n }\n\n}\n\n')])])]),r("h4",{attrs:{id:"_8-3-5-使用触发器文件"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-5-使用触发器文件"}},[t._v("#")]),t._v(" 8.3.5.使用触发器文件")]),t._v(" "),r("p",[t._v("如果你使用的 IDE 不断编译更改的文件,那么你可能更喜欢只在特定的时间触发重启。要做到这一点,你可以使用“触发器文件”,这是一种特殊的文件,当你想要实际触发重新启动检查时,必须对其进行修改。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("对该文件的任何更新都会触发检查,但只有在 DevTools 检测到有事情要做时,才会真正重新启动。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("p",[t._v("要使用触发器文件,请将"),r("code",[t._v("spring.devtools.restart.trigger-file")]),t._v("属性设置为触发器文件的名称(不包括任何路径)。触发器文件必须出现在你的 Classpath 上的某个地方。")]),t._v(" "),r("p",[t._v("例如,如果你的项目具有以下结构:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("src\n+- main\n +- resources\n +- .reloadtrigger\n")])])]),r("p",[t._v("那么你的"),r("code",[t._v("trigger-file")]),t._v("属性将是:")]),t._v(" "),r("p",[t._v("属性")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("spring.devtools.restart.trigger-file=.reloadtrigger\n")])])]),r("p",[t._v("Yaml")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('spring:\n devtools:\n restart:\n trigger-file: ".reloadtrigger"\n')])])]),r("p",[t._v("现在只有更新"),r("code",[t._v("src/main/resources/.reloadtrigger")]),t._v("时才会重新启动。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("你可能希望将"),r("code",[t._v("spring.devtools.restart.trigger-file")]),t._v("设置为"),r("a",{attrs:{href:"#using.devtools.globalsettings"}},[t._v("全局设置")]),t._v(",以便所有项目都以相同的方式运行。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("p",[t._v("有些 IDE 有一些特性,可以使你不必手动更新触发器文件。"),r("a",{attrs:{href:"https://spring.io/tools",target:"_blank",rel:"noopener noreferrer"}},[t._v("Spring Tools for Eclipse"),r("OutboundLink")],1),t._v("和"),r("a",{attrs:{href:"https://www.jetbrains.com/idea/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Intellij Idea(终极版)"),r("OutboundLink")],1),t._v("都有这样的支持。使用 Spring 工具,你可以使用控制台视图中的“Reload”按钮(只要你的"),r("code",[t._v("trigger-file")]),t._v("名为"),r("code",[t._v(".reloadtrigger")]),t._v(")。对于 IntelliJ IDEA,你可以遵循"),r("a",{attrs:{href:"https://www.jetbrains.com/help/idea/spring-boot.html#application-update-policies",target:"_blank",rel:"noopener noreferrer"}},[t._v("文件中的说明"),r("OutboundLink")],1),t._v("。")]),t._v(" "),r("h4",{attrs:{id:"_8-3-6-自定义重新启动类装入器"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-6-自定义重新启动类装入器"}},[t._v("#")]),t._v(" 8.3.6.自定义重新启动类装入器")]),t._v(" "),r("p",[t._v("如前面"),r("a",{attrs:{href:"#using.devtools.restart.restart-vs-reload"}},[t._v("重新启动 VS 重新加载")]),t._v("小节中所述,重新启动功能是通过使用两个类装入器来实现的。如果这会导致问题,你可能需要定制由哪个类装入器加载的内容。")]),t._v(" "),r("p",[t._v("默认情况下,你的 IDE 中的任何打开的项目都是用“restart”类加载器加载的,而任何常规的"),r("code",[t._v(".jar")]),t._v("文件都是用“base”类加载器加载的。如果使用"),r("code",[t._v("mvn spring-boot:run")]),t._v("或"),r("code",[t._v("gradle bootRun")]),t._v(",情况也是如此:包含"),r("code",[t._v("@SpringBootApplication")]),t._v("的项目是用“重新启动”类装入器加载的,其他所有内容都是用“基本”类装入器加载的。")]),t._v(" "),r("p",[t._v("通过创建"),r("code",[t._v("META-INF/spring-devtools.properties")]),t._v("文件,你可以指示 Spring boot 使用不同的类装入器加载项目的部分内容。"),r("code",[t._v("spring-devtools.properties")]),t._v("文件可以包含以"),r("code",[t._v("restart.exclude")]),t._v("和"),r("code",[t._v("restart.include")]),t._v("为前缀的属性。"),r("code",[t._v("include")]),t._v("元素是应该被拉到“重新启动”类装入器中的项,而"),r("code",[t._v("exclude")]),t._v("元素是应该被下推到“基本”类装入器中的项。该属性的值是应用于 Classpath 的正则表达式模式,如以下示例所示:")]),t._v(" "),r("p",[t._v("属性")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("restart.exclude.companycommonlibs=/mycorp-common-[\\\\w\\\\d-\\\\.]+\\\\.jar\nrestart.include.projectcommon=/mycorp-myproj-[\\\\w\\\\d-\\\\.]+\\\\.jar\n")])])]),r("p",[t._v("Yaml")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('restart:\n exclude:\n companycommonlibs: "/mycorp-common-[\\\\w\\\\d-\\\\.]+\\\\.jar"\n include:\n projectcommon: "/mycorp-myproj-[\\\\w\\\\d-\\\\.]+\\\\.jar"\n')])])]),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("所有属性键必须是唯一的。"),r("br"),t._v("只要一个属性以"),r("code",[t._v("restart.include.")]),t._v("或"),r("code",[t._v("restart.exclude.")]),t._v("开头,就被认为是唯一的。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("Classpath 中的所有"),r("code",[t._v("META-INF/spring-devtools.properties")]),t._v("都已加载。"),r("br"),t._v("你可以将文件打包到你的项目中,或者打包到项目使用的库中。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h4",{attrs:{id:"_8-3-7-已知限制"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-3-7-已知限制"}},[t._v("#")]),t._v(" 8.3.7.已知限制")]),t._v(" "),r("p",[t._v("对于使用标准"),r("code",[t._v("ObjectInputStream")]),t._v("进行反序列化的对象,重启功能不能很好地工作。如果需要反序列化数据,则可能需要结合使用 Spring 的"),r("code",[t._v("ConfigurableObjectInputStream")]),t._v("和"),r("code",[t._v("Thread.currentThread().getContextClassLoader()")]),t._v("。")]),t._v(" "),r("p",[t._v("遗憾的是,一些第三方库在反序列化时没有考虑上下文类装入器。如果你发现了这样的问题,你需要向原始作者请求修复。")]),t._v(" "),r("h3",{attrs:{id:"_8-4-livereload"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-4-livereload"}},[t._v("#")]),t._v(" 8.4.LiveReload")]),t._v(" "),r("p",[r("code",[t._v("spring-boot-devtools")]),t._v("模块包括一个嵌入式 LiveReload 服务器,该服务器可用于在更改资源时触发浏览器刷新。LiveReload 浏览器扩展从"),r("a",{attrs:{href:"http://livereload.com/extensions/",target:"_blank",rel:"noopener noreferrer"}},[t._v("LiveReload.com"),r("OutboundLink")],1),t._v("免费提供给 Chrome、Firefox 和 Safari。")]),t._v(" "),r("p",[t._v("如果不想在应用程序运行时启动 LiveReload 服务器,可以将"),r("code",[t._v("spring.devtools.livereload.enabled")]),t._v("属性设置为"),r("code",[t._v("false")]),t._v("。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("一次只能运行一个 LiveReload 服务器。"),r("br"),t._v("在启动应用程序之前,请确保没有其他 LiveReload 服务器在运行。"),r("br"),t._v("如果你从 IDE 启动多个应用程序,则只有第一个应用程序具有 LiveReload 支持。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("要在文件更改时触发 LiveReload,必须启用"),r("a",{attrs:{href:"#using.devtools.restart"}},[t._v("自动重启")]),t._v("。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h3",{attrs:{id:"_8-5-全局设置"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-5-全局设置"}},[t._v("#")]),t._v(" 8.5.全局设置")]),t._v(" "),r("p",[t._v("你可以通过向"),r("code",[t._v("$HOME/.config/spring-boot")]),t._v("目录添加以下任意文件来配置全局 devtools 设置:")]),t._v(" "),r("ol",[r("li",[r("p",[r("code",[t._v("spring-boot-devtools.properties")])])]),t._v(" "),r("li",[r("p",[r("code",[t._v("spring-boot-devtools.yaml")])])]),t._v(" "),r("li",[r("p",[r("code",[t._v("spring-boot-devtools.yml")])])])]),t._v(" "),r("p",[t._v("添加到这些文件中的任何属性都适用于机器上使用 DevTools 的"),r("em",[t._v("全部")]),t._v(" Spring 引导应用程序。例如,要将 Restart 配置为始终使用"),r("a",{attrs:{href:"#using.devtools.restart.triggerfile"}},[t._v("触发器文件")]),t._v(",你可以将以下属性添加到"),r("code",[t._v("spring-boot-devtools")]),t._v("文件中:")]),t._v(" "),r("p",[t._v("Properties")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("spring.devtools.restart.trigger-file=.reloadtrigger\n")])])]),r("p",[t._v("Yaml")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('spring:\n devtools:\n restart:\n trigger-file: ".reloadtrigger"\n')])])]),r("p",[t._v("默认情况下,"),r("code",[t._v("$HOME")]),t._v("是用户的主目录。要自定义此位置,请设置"),r("code",[t._v("SPRING_DEVTOOLS_HOME")]),t._v("环境变量或"),r("code",[t._v("spring.devtools.home")]),t._v("系统属性。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果在"),r("code",[t._v("$HOME/.config/spring-boot")]),t._v("中未找到 DevTools 配置文件,在"),r("code",[t._v("$HOME")]),t._v("目录的根目录中搜索是否存在"),r("code",[t._v(".spring-boot-devtools.properties")]),t._v("文件。"),r("br"),t._v("这允许你与不支持"),r("code",[t._v("$HOME/.config/spring-boot")]),t._v("位置的旧版本 Spring 启动上的应用程序共享 DevTools 全局配置。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("在 DevTools Properties/YAML 文件中不支持配置文件。"),r("br"),r("br"),t._v("在"),r("code",[t._v(".spring-boot-devtools.properties")]),t._v("中激活的任何配置文件都不会影响"),r("RouterLink",{attrs:{to:"/spring-boot/features.html#features.external-config.files.profile-specific"}},[t._v("特定于配置文件的配置文件")]),t._v("的加载。"),r("br"),t._v("配置文件特定的文件名("),r("code",[t._v("spring-boot-devtools-.properties")]),t._v(")和"),r("code",[t._v("spring.config.activate.on-profile")]),t._v("文件在 YAML 和属性文件中都不受支持。")],1)])]),t._v(" "),r("tbody")]),t._v(" "),r("h4",{attrs:{id:"_8-5-1-配置文件系统监视器"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-5-1-配置文件系统监视器"}},[t._v("#")]),t._v(" 8.5.1.配置文件系统监视器")]),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSystemWatcher.java",target:"_blank",rel:"noopener noreferrer"}},[t._v("Filesystemwatcher"),r("OutboundLink")],1),t._v("的工作原理是轮询具有一定时间间隔的类更改,然后等待预定义的静默期,以确保没有更多更改。由于 Spring 启动完全依赖于 IDE 来编译并将文件复制到 Spring 启动可以读取它们的位置,因此你可能会发现,当 DevTools 重新启动应用程序时,有时某些更改没有得到反映。如果你经常看到这样的问题,请尝试将"),r("code",[t._v("spring.devtools.restart.poll-interval")]),t._v("和"),r("code",[t._v("spring.devtools.restart.quiet-period")]),t._v("参数增加到适合你的开发环境的值:")]),t._v(" "),r("p",[t._v("Properties")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("spring.devtools.restart.poll-interval=2s\nspring.devtools.restart.quiet-period=1s\n")])])]),r("p",[t._v("Yaml")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v('spring:\n devtools:\n restart:\n poll-interval: "2s"\n quiet-period: "1s"\n')])])]),r("p",[t._v("Classpath 监视的目录现在每 2 秒对更改进行一次轮询,并保持 1 秒的静默期,以确保没有额外的类更改。")]),t._v(" "),r("h3",{attrs:{id:"_8-6-远程应用程序"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-6-远程应用程序"}},[t._v("#")]),t._v(" 8.6.远程应用程序")]),t._v(" "),r("p",[t._v("Spring 引导开发人员工具不限于本地开发。在远程运行应用程序时,你还可以使用几个功能。远程支持是 OPT 的,因为启用它可能会带来安全风险。只有在运行在受信任的网络上或使用 SSL 进行安全保护时,才应启用它。如果这两个选项都不对你可用,那么你不应该使用 DevTools 的远程支持。你永远不应该在生产部署中启用支持。")]),t._v(" "),r("p",[t._v("要启用它,你需要确保"),r("code",[t._v("devtools")]),t._v("包含在重新打包的归档文件中,如以下清单所示:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v("\n \n \n org.springframework.boot\n spring-boot-maven-plugin\n \n false\n \n \n \n\n")])])]),r("p",[t._v("然后需要设置"),r("code",[t._v("spring.devtools.remote.secret")]),t._v("属性。像任何重要的密码或秘密一样,该值应该是唯一的和强大的,以便它不能被猜测或暴力强迫。")]),t._v(" "),r("p",[t._v("远程 DevTools 支持由两部分提供:接受连接的服务器端端点和在 IDE 中运行的客户机应用程序。设置"),r("code",[t._v("spring.devtools.remote.secret")]),t._v("属性后,服务器组件将自动启用。客户端组件必须手动启动。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("Spring WebFlux 应用程序不支持远程 DevTools。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h4",{attrs:{id:"_8-6-1-运行远程客户端应用程序"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-6-1-运行远程客户端应用程序"}},[t._v("#")]),t._v(" 8.6.1.运行远程客户端应用程序")]),t._v(" "),r("p",[t._v("远程客户机应用程序被设计为在你的 IDE 中运行。你需要以与你所连接的远程项目相同的 Classpath 运行"),r("code",[t._v("org.springframework.boot.devtools.RemoteSpringApplication")]),t._v("。应用程序唯一需要的参数是它所连接的远程 URL。")]),t._v(" "),r("p",[t._v("例如,如果你正在使用 Eclipse 或 Spring 工具,并且你有一个名为"),r("code",[t._v("my-app")]),t._v("的项目已部署到 Cloud Foundry,那么你将执行以下操作:")]),t._v(" "),r("ul",[r("li",[r("p",[t._v("从"),r("code",[t._v("Run")]),t._v("菜单中选择"),r("code",[t._v("Run Configurations…​")]),t._v("。")])]),t._v(" "),r("li",[r("p",[t._v("创建一个新的"),r("code",[t._v("Java Application")]),t._v("“启动配置”。")])]),t._v(" "),r("li",[r("p",[t._v("浏览"),r("code",[t._v("my-app")]),t._v("项目。")])]),t._v(" "),r("li",[r("p",[t._v("使用"),r("code",[t._v("org.springframework.boot.devtools.RemoteSpringApplication")]),t._v("作为主类。")])]),t._v(" "),r("li",[r("p",[t._v("将"),r("code",[t._v("https://myapp.cfapps.io")]),t._v("添加到"),r("code",[t._v("Program arguments")]),t._v("(或你的远程 URL 是什么)。")])])]),t._v(" "),r("p",[t._v("正在运行的远程客户端可能类似于以下清单:")]),t._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[t._v(" . ____ _ __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | | _ \\___ _ __ ___| |_ ___ \\ \\ \\ \\\n \\\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \\/ _ \\ _/ -_) ) ) ) )\n ' |____| .__|_| |_|_| |_\\__, | |_|_\\___|_|_|_\\___/\\__\\___|/ / / /\n =========|_|==============|___/===================================/_/_/_/\n :: Spring Boot Remote :: 2.6.4\n\n2015-06-10 18:25:06.632 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication on pwmbp with PID 14938 (/Users/pwebb/projects/spring-boot/code/spring-boot-project/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code)\n2015-06-10 18:25:06.671 INFO 14938 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.spring[email protected]2a17b7b6: startup date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy\n2015-06-10 18:25:07.043 WARN 14938 --- [ main] o.s.b.d.r.c.RemoteClientConfiguration : The connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'.\n2015-06-10 18:25:07.074 INFO 14938 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729\n2015-06-10 18:25:07.130 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105)\n")])])]),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("Classpath 因为远程客户端使用的是与实际应用相同的应用程序,所以它可以直接读取应用程序属性。"),r("br"),t._v("这就是"),r("code",[t._v("spring.devtools.remote.secret")]),t._v("属性的读取方式,并将其传递给服务器以进行身份验证。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("始终建议使用"),r("code",[t._v("https://")]),t._v("作为连接协议,以便对流量进行加密,并且不能拦截密码。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("如果需要使用代理来访问远程应用程序,请配置"),r("code",[t._v("spring.devtools.remote.proxy.host")]),t._v("和"),r("code",[t._v("spring.devtools.remote.proxy.port")]),t._v("属性。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h4",{attrs:{id:"_8-6-2-远程更新"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_8-6-2-远程更新"}},[t._v("#")]),t._v(" 8.6.2.远程更新")]),t._v(" "),r("p",[t._v("远程客户端以与"),r("a",{attrs:{href:"#using.devtools.restart"}},[t._v("本地重启")]),t._v("相同的方式监视你的应用程序 Classpath 的更改。任何更新的资源都会被推送到远程应用程序,并(* 如果需要 *)触发重新启动。如果你迭代使用本地不具备的云服务的功能,这将很有帮助。通常,远程更新和重启要比完整的重建和部署周期快得多。")]),t._v(" "),r("p",[t._v("在较慢的开发环境中,可能会出现静默期不够的情况,并且类中的更改可能会被分解为批处理。上载第一批类更改后,服务器将重新启动。由于服务器正在重新启动,下一个批处理不能发送到应用程序。")]),t._v(" "),r("p",[t._v("这通常表现为"),r("code",[t._v("RemoteSpringApplication")]),t._v("日志中有关于未能上载某些类的警告,以及随后的重试。但这也可能导致应用程序代码不一致,以及在上传第一批更改后无法重新启动。如果你经常观察到这样的问题,请尝试将"),r("code",[t._v("spring.devtools.restart.poll-interval")]),t._v("和"),r("code",[t._v("spring.devtools.restart.quiet-period")]),t._v("参数增加到适合你的开发环境的值。有关这些属性的配置,请参见"),r("a",{attrs:{href:"#using.devtools.globalsettings.configuring-file-system-watcher"}},[t._v("配置文件系统监视器")]),t._v("小节。")]),t._v(" "),r("table",[r("thead",[r("tr",[r("th"),t._v(" "),r("th",[t._v("只有在远程客户端运行时才会监视文件。"),r("br"),t._v("如果在启动远程客户端之前更改了文件,则不会将其推送到远程服务器。")])])]),t._v(" "),r("tbody")]),t._v(" "),r("h2",{attrs:{id:"_9-包装你的应用程序以进行生产"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_9-包装你的应用程序以进行生产"}},[t._v("#")]),t._v(" 9. 包装你的应用程序以进行生产")]),t._v(" "),r("p",[t._v("可执行 JAR 可用于生产部署。由于它们是自包含的,因此也非常适合基于云的部署。")]),t._v(" "),r("p",[t._v("对于额外的“生产就绪”特性,例如健康状态、审计和度量休息或 JMX 端点,可以考虑添加"),r("code",[t._v("spring-boot-actuator")]),t._v("。详见 "),r("em",[r("RouterLink",{attrs:{to:"/spring-boot/actuator.html#actuator"}},[t._v("actuator.html")])],1),t._v("。")]),t._v(" "),r("h2",{attrs:{id:"_10-接下来要读什么"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#_10-接下来要读什么"}},[t._v("#")]),t._v(" 10. 接下来要读什么?")]),t._v(" "),r("p",[t._v("你现在应该了解如何使用 Spring 引导和一些你应该遵循的最佳实践。你现在可以继续深入了解特定的 "),r("em",[r("RouterLink",{attrs:{to:"/spring-boot/features.html#features"}},[t._v("Spring Boot features")])],1),t._v(",或者你可以跳过并阅读 Spring boot 的“"),r("RouterLink",{attrs:{to:"/spring-boot/actuator.html#actuator"}},[t._v("生产准备就绪")]),t._v("”方面。")],1)])}),[],!1,null,null,null);e.default=a.exports}}]);