# 20\. 开发工具 Spring Boot包括一组额外的工具,可以使应用程序开发体验更加愉快。 spring-boot-devtools模块可以包含在任何项目中,以提供额外的[开发时](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/development-time)功能。 要包含devtools支持,只需将模块依赖关系添加到您的构建中: Maven: ``` org.springframework.boot spring-boot-devtools true ``` Gradle: ``` dependencies { compile("org.springframework.boot:spring-boot-devtools") } ``` > 当运行完全打包的应用程序时,开发人员工具将自动禁用。 如果您的应用程序是使用java -jar启动的,或者是使用特殊的类加载器启动,那么它将会被认为是“生产环境的应用程序”。 将开发工具依赖关系标记为可选(optional)是一种最佳做法,可以防止使用项目将devtools传递性地应用于其他模块。 Gradle不支持开箱即用的可选依赖项,因此您可能希望在此期间查看[propdeps-plugin](https://github.com/spring-projects/gradle-plugins/tree/master/propdeps-plugin)。 > 重新打包的jar包默认情况下不包含devtools。 如果要使用某些[远程devtools功能](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#using-boot-devtools-remote),您需要禁用excludeDevtools 构建下的属性以包含devtools。 该属性支持Maven和Gradle插件。 ### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#201-属性默认值)20.1 属性默认值 Spring Boots支持的几个库使用缓存来提高性能。 例如,[模板引擎](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-template-engines)将缓存编译的模板,以避免重复解析模板文件。 此外,Spring MVC可以在返回静态资源时向响应中添加HTTP缓存头。 虽然缓存在生产中非常有益,但它在开发过程中可能会产生反效果,从而阻止您看到刚刚在应用程序中进行的更改。 因此,spring-boot-devtools将默认禁用这些缓存选项。 缓存选项通常由您的application.properties文件中的设置配置。 例如,Thymeleaf提供了spring.thymeleaf.cache属性。 spring-boot-devtools模块不需要手动设置这些属性,而是自动应用更加合理的开发时(development-time)配置。 > 有关应用的属性的完整列表,请参阅 [DevToolsPropertyDefaultsPostProcessor](https://github.com/spring-projects/spring-boot/tree/v1.5.2.RELEASE/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java)。 ### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#202-自动重启)20.2 自动重启 使用spring-boot-devtools的应用程序将在类路径上的文件发生更改时自动重新启动。 这在IDE中开发时可能是一个有用的功能,因为它为代码更改提供了非常快的反馈循环。 默认情况下,将监视指向文件夹的类路径上的任何条目。 请注意,某些资源(如静态资源和视图模板)不需要重新启动应用程序。 触发重启 当DevTools监视类路径资源时,触发重新启动的唯一方法是更新类路径中的文件时。 导致类路径更新的方式取决于您正在使用的IDE。 在Eclipse中,保存修改的文件将导致类路径被更新并触发重新启动。 在IntelliJ IDEA中,构建项目(Build→Make Project)将具有相同的效果。 > 只要 forking 被启用,您也可以通过支持的构建插件(即Maven和Gradle)启动应用程序,因为DevTools需要一个单独的应用程序类加载器才能正常运行。Gradle和Maven默认情况下在类路径上检DevTools。 > 自动重启当与LiveReload一起使用时工作非常好。 详见[下文](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#using-boot-devtools-livereload)。 如果您使用JRebel,自动重启将被禁用,有利于动态类重新加载。 其他devtools功能仍然可以使用(如LiveReload和属性覆盖)。 > DevTools依赖于应用程序上下文的关闭钩子,以在重新启动期间关闭它。 如果禁用了关闭挂钩(SpringApplication.setRegisterShutdownHook(false)),DevTools将无法正常工作。 > 当判断类路径中的项目是否会在更改时触发重新启动时,DevTools会自动忽略名为spring-boot,spring-boot-devtools,spring-boot-autoconfigure,spring-boot-actuator和spring-boot-start的项目。 重新启动(Restart) vs 重新加载(Reload) Spring Boot提供的重新启动技术使用两个类加载器。 不会改的类(例如,来自第三方的jar)被加载到基类加载器中。 您正在开发的类被加载到重新启动(restart)类加载器中。 当应用程序重新启动时,重新启动类加载器将被丢弃,并创建一个新的类加载器。 这种方法意味着应用程序重新启动通常比“冷启动”快得多,因为基类加载器已经可以使用。 如果发现重新启动对应用程序不够快,或遇到类加载问题,您可以考虑来自ZeroTurnaround的JRebel等重新加载技术。 这些工作通过在加载类时重写(rewriting)类,使其更适合重新加载。 Spring Loaded提供了另一个选项,但是它在很多框架上不支持,并且不支持商用。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2021-排除资源)20.2.1 排除资源 在类路径下,某些资源在更改时不一定需要触发重新启动。 例如,Thymeleaf模板可以直接编辑不需重启。 默认情况下,有一些排除项,更改 /META-INF/maven,/META-INF/resources,/resources,/static,/public或/templates中的资源不会触发重新启动,但会触发[实时重新加载](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#using-boot-devtools-livereload)。 如果要自定义这些排除项,可以使用spring.devtools.restart.exclude属性。 例如,要仅排除 /static和 /public,您可以设置: ``` spring.devtools.restart.exclude=static/**,public/** ``` > 如果要保留这些默认值并添加其他排除项,请改用spring.devtools.restart.additional-exclude属性。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2022-监视额外的路径)20.2.2 监视额外的路径 有时当您对不在类路径中的文件进行更改时,需要重新启动或重新加载应用程序。为此,请使用spring.devtools.restart.additional-paths属性来配置其他路径以监视更改。 您可以使用[上述](described above)的spring.devtools.restart.exclude属性来控制附加路径下的更改是否会触发完全重新启动或只是[实时重新加载](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#using-boot-devtools-livereload)。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2023-禁用重启)20.2.3 禁用重启 如果不想使用重新启动功能,可以使用spring.devtools.restart.enabled属性来禁用它。 在大多数情况下,您可以在application.properties中设置此项(这仍将初始化重新启动类加载器,但不会监视文件更改)。 例如,如果您需要完全禁用重新启动支持,因为它在一些特定库中不能正常运行,则需要在调用SpringApplication.run(...)之前设置System属性。 例如: ``` public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(MyApp.class, args); } ``` #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2024-使用触发文件)20.2.4 使用触发文件 如果您使用IDE工具编写代码,更改文件,则您可能希望仅在特定时间触发重新启动。 为此,您可以使用“触发文件”,这是一个特殊文件,当您要实际触发重新启动检查时,必须修改它。 更改文件只会触发检查,只有在Devtools检测到它必须执行某些操作时才会重新启动。 触发文件可以手动更新,也可以通过IDE插件更新。 要使用触发器文件,请使用spring.devtools.restart.trigger-file属性。 > 您可能希望将spring.devtools.restart.trigger-file设置为[全局设置](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#using-boot-devtools-globalsettings),以使所有项目的行为方式相同。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2025-自定义重新启动类加载器)20.2.5 自定义重新启动类加载器 如上面的 [Restart vs Reload](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#using-spring-boot-restart-vs-reload) 部分所述,重新启动功能是通过使用两个类加载器实现的。 对于大多数应用程序,此方法运行良好,但有时可能会导致类加载问题。 默认情况下,IDE中的任何打开的项目将使用“重新启动”类加载器加载,任何常规.jar文件将使用“base”类加载器加载。 如果您在多模块项目上工作,而不是每个模块都导入到IDE中,则可能需要自定义事件。 为此,您可以创建一个META-INF / spring-devtools.properties文件。 spring-devtools.properties文件可以包含restart.exclude 和 restart.include.prefixed属性。 include元素是应该被拉入“重新启动(restart)”类加载器的项目,排除元素是应该向下推入“基本(base)”类加载器的项目。 属性的值是将应用于类路径的正则表达式模式。 例如: ``` restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar ``` > 所有属性键必须是唯一的。 只要一个属性从restart.include. 或restart.exclude. 开始,将被考虑。 > 将加载类路径中的所有META-INF/spring-devtools.properties。 您可以在项目中打包文件,或者在项目所使用的库中打包文件。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2026-已知的限制)20.2.6 已知的限制 重新启动功能对于使用标准ObjectInputStream反序列化的对象无效。 如果需要反序列化数据,可能需要使用Spring的ConfigurableObjectInputStream与Thread.currentThread()。getContextClassLoader()组合使用。 不幸的是,几个第三方库在不考虑上下文类加载器的情况下反序列化。 如果您发现这样的问题,您需要向原始作者请求修复。 ### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#203-livereload)20.3 LiveReload spring-boot-devtools模块包括一个嵌入式LiveReload服务器,可以在资源更改时用于触发浏览器刷新。 LiveReload浏览器扩展程序可以从 [http://livereload.com](http://livereload.com/)免费获取Chrome,Firefox和Safari的插件。 如果您不想在应用程序运行时启动LiveReload服务器,则可以将spring.devtools.livereload.enabled属性设置为false。 > 一次只能运行一个LiveReload服务器。 开始应用程序之前,请确保没有其他LiveReload服务器正在运行。 如果从IDE启动多个应用程序,则只有第一个应用程序将支持LiveReload。 ### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#204-全局设置)20.4 全局设置 您可以通过向 $HOME 文件夹添加名为.spring-boot-devtools.properties的文件来配置全局devtools设置(请注意文件名以“.”开头)。 添加到此文件的任何属性将适用于您的计算机上使用devtools的所有Spring Boot应用程序。 例如,要配置重新启动以始终使用触发器文件,您可以添加以下内容: ~/.spring-boot-devtools.properties. ``` spring.devtools.reload.trigger-file=.reloadtrigger ``` ### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#205远程应用)20.5远程应用 Spring Boot开发工具不仅限于本地开发。 远程运行应用程序时也可以使用多种功能。 远程支持是可选择的,要使其能够确保重新打包的存档中包含devtools: ``` org.springframework.boot spring-boot-maven-plugin false ``` 那么你需要设置一个spring.devtools.remote.secret属性,例如: ``` spring.devtools.remote.secret=mysecret ``` > 在远程应用程序上启用spring-boot-devtools是一种安全隐患。 您不应该在生产部署中启用该支持。 远程devtools支持分为两部分: 有一个接受连接的服务器端和您在IDE中运行的客户端应用程序。 当spring.devtools.remote.secret属性设置时,服务器组件将自动启用。 客户端组件必须手动启动。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2051-运行远程客户端应用程序)20.5.1 运行远程客户端应用程序 远程客户端应用程序旨在从IDE中运行。 您需要使用与要连接的远程项目相同的类路径运行org.springframework.boot.devtools.RemoteSpringApplication。 传递给应用程序的必选参数应该是您要连接到的远程URL。 例如,如果您使用Eclipse或STS,并且有一个名为my-app的项目已部署到Cloud Foundry,则可以执行以下操作: * 从Run 菜单中选择Run Configurations…。 * 创建一个新的Java Application “launch configuration”。 * 浏览my-app项目。 * 使用org.springframework.boot.devtools.RemoteSpringApplication作为主类。 * 将 运行的远程客户端将如下所示: ``` . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \ \\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) ) ' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / / =========|_|==============|___/===================================/_/_/_/ :: Spring Boot Remote :: 1.5.2.RELEASE 2015-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-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code/spring-boot-samples/spring-boot-sample-devtools) 2015-06-10 18:25:06.671 INFO 14938 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2a17b7b6: startup date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy 2015-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://'. 2015-06-10 18:25:07.074 INFO 14938 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729 2015-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) ``` > 由于远程客户端正在使用与实际应用程序相同的类路径,因此可以直接读取应用程序属性。 这是spring.devtools.remote.secret属性如何读取并传递到服务器进行身份验证。 > 建议使用https//作为连接协议,以便流量被加密,防止密码被拦截。 > 如果需要使用代理访问远程应用程序,请配置spring.devtools.remote.proxy.host和spring.devtools.remote.proxy.port属性。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2052-远程更新)20.5.2 远程更新 远程客户端将以与本地相同的方式监视应用程序类路径的更改。 任何更新的资源将被推送到远程应用程序,并且(如果需要的话)触发重新启动。 如果您正在迭代使用您当地没有的云服务的功能,这可能会非常有用。 通常,远程更新和重新启动比完全重建和部署周期要快得多。 > 仅在远程客户端运行时才监视文件。 如果在启动远程客户端之前更改文件,则不会将其推送到远程服务器。 #### [](file:///C:/Users/geekidentity/AppData/Local/Youdao/YNote/markdown/index.html#2053-远程调试隧道)20.5.3 远程调试隧道 在远程应用程序诊断问题时,Java远程调试非常有用。 不幸的是,当您的应用程序部署在数据中心之外时,并不总是能够进行远程调试。 如果您正在使用基于容器的技术(如Docker),远程调试也可能难以设置。 为了帮助解决这些限制,devtools支持基于HTTP隧道的传输远程调试传输。 远程客户端在端口8000上提供本地服务器,您可以连接远程调试器。 建立连接后,通过HTTP将调试数据发送到远程应用程序。 如果要使用其他端口,可以使用spring.devtools.remote.debug.local-port属性更改。 您需要确保远程应用程序启用远程调试启用。 通常可以通过配置JAVA_OPTS来实现。 例如,使用Cloud Foundry,您可以将以下内容添加到manifest.yml中: ``` --- env: JAVA_OPTS: "-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n" ``` > 请注意,您不需要将 address=NNNN 选项传递给-Xrunjdwp。 如果省略Java将随机选择一个的空闲端口。 > 通过网络调试远程服务可能很慢,您可能需要在IDE中增加超时时间。 例如,在Eclipse中,您可以从Preferences...中选择Java→Debug ,并将Debugger timeout (ms)更改为更合适的值(大多数情况下,60000可以正常工作)。 > 当使用IntelliJ IDEA的远程调试隧道时,必须将所有调试断点配置为挂起线程而不是挂起VM。 默认情况下,IntelliJ IDEA中的断点会挂起整个VM,而不是仅挂起触发断点的线程。 这会导致挂起管理远程调试通道的线程等不必要的副作用,导致调试会话冻结。 当使用IntelliJ IDEA的远程调试隧道时,应将所有断点配置为挂起线程而不是VM。 有关详细信息,请参阅[IDEA-165769](https://youtrack.jetbrains.com/issue/IDEA-165769)。