Spring Boot содержит дополнительный набор инструментальных средств, которые могут сделать процесс разработки приложений немного приятнее. Модуль spring-boot-devtools можно добавлять в любой проект для обеспечения дополнительных функций во время разработки. Чтобы внедрить поддержку инструментальных средств разработки (devtools), добавьте зависимость модуля в вашу сборку, как показано в следующих листингах для Maven и Gradle:

Maven
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
Gradle
dependencies {
    developmentOnly("org.springframework.boot:spring-boot-devtools")
}
Инструментальные средства разработки автоматически деактивируются при выполнении полностью упакованного приложения. Если ваше приложение запускается через java -jar или через специальный загрузчик классов, то оно считается "производственным приложением". Вы можете управлять этой логикой работы при помощи системного свойства spring.devtools.restart.enabled. Чтобы активировать devtools, независимо от загрузчика классов, используемого для запуска приложения, установите системное свойство -Dspring.devtools.restart.enabled=true. Этого нельзя делать в производственной среде, где выполнение devtools представляет угрозу безопасности. Чтобы отключить devtools, исключите зависимость или установите системное свойство -Dspring.devtools.restart.enabled=false.
Если пометить зависимость в Maven как необязательную или использовать конфигурацию developmentOnly в Gradle (как показано выше), то это предотвратит транзитное применение devtools к другим модулям, использующим ваш проект.
Переупакованные архивы по умолчанию не содержат devtools. Если вы хотите использовать определенную функцию удаленного использования devtools, то вам необходимо включить её в состав. При использовании плагина для Maven установите для свойства excludeDevtools значение false. При использовании плагина для Gradle сконфигурируйте classpath для задачи так, чтобы он содержал конфигурацию developmentOnly.

Диагностика проблем с загрузкой классов

Функциональность перезапуска реализована с помощью двух загрузчиков классов. Для большинства приложений такой подход работает хорошо. Однако иногда он может приводить к проблемам с загрузкой классов, особенно в многомодульных проектах.

Чтобы диагностировать, действительно ли проблемы с загрузкой классов вызваны devtools и их двумя загрузчиками классов, попробуйте отключить перезапуск. Если это решит ваши проблемы, настройте перезапуск загрузчика классов на охват всего вашего проекта.

Свойства по умолчанию

Некоторые библиотеки, поддерживаемые Spring Boot, используют кэши для повышения производительности. Например, шаблонизаторы кэшируют скомпилированные шаблоны, чтобы избежать повторного парсинга файлов шаблонов. Кроме того, Spring MVC может добавлять заголовки HTTP-кэширования в ответы при обработке статических ресурсов.

Хотя кэширование очень полезно в производственной среде, оно может быть контрпродуктивным во время разработки, не позволяя видеть изменения, которые были только что внесены в приложение. По этой причине spring-boot-devtools по умолчанию деактивирует настройки кэширования.

Настройки кэша обычно конфигурируются с помощью параметров в файле application.properties. Например, Thymeleaf предусматривает свойство spring.thymeleaf.cache. Вместо того, чтобы устанавливать эти свойства вручную, модуль spring-boot-devtools автоматически применяет адекватную конфигурацию на время разработки.

В следующей таблице перечислены все применяемые свойства:

Имя Значение по умолчанию

server.error.include-binding-errors

always

server.error.include-message

always

server.error.include-stacktrace

always

server.servlet.jsp.init-parameters.development

true

server.servlet.session.persistent

true

spring.freemarker.cache

false

spring.graphql.graphiql.enabled

true

spring.groovy.template.cache

false

spring.h2.console.enabled

true

spring.mustache.servlet.cache

false

spring.mvc.log-resolved-exception

true

spring.reactor.debug

true

spring.template.provider.cache

false

spring.thymeleaf.cache

false

spring.web.resources.cache.period

0

spring.web.resources.chain.cache

false

Если вам не нужно, чтобы применялись параметры по умолчанию, то можете установить spring.devtools.add-properties в false в файле application.properties.

Поскольку при разработке приложений на Spring MVC и Spring WebFlux требуется больше информации о веб-запросах, инструментальные средства разработки предполагают активацию DEBUG журналирования для web-группы журналирования. Это даст информацию о входящем запросе, о том, какой обработчик его обрабатывает, о результате ответа и другие подробности. Если требуется регистрировать все сведения запроса (включая потенциально конфиденциальную информацию), то можно включить свойства конфигурации spring.mvc.log-request-details или spring.codec.log-request-details.

Автоматический перезапуск

Приложения, использующие spring-boot-devtools, автоматически перезапускаются при изменении файлов в classpath. Это может быть полезной функцией при работе в IDE, так как обеспечивает очень быструю обратную связь для внесения изменений в код. По умолчанию любая запись в classpath, указывающая на каталог, отслеживается на предмет изменений. Обратите внимание, что некоторые ресурсы, такие как статическое содержимое и шаблоны представлений, не требуют перезапуска приложения.

Инициация перезапуска

Поскольку DevTools отслеживает ресурсы в classpath, единственным способом инициировать перезапуск является обновление classpath. Независимо от того, используете ли вы IDE или один из плагинов сборки, измененные файлы нужно перекомпилировать, чтобы инициировать перезапуск. Способ обновления classpath зависит от используемого инструмента:

  • В Eclipse сохранение измененного файла приводит к обновлению classpath и инициирует перезапуск.

  • В IntelliJ IDEA сборка проекта (Build +→+ Build Project) имеет тот же эффект.

  • Если используется плагин сборки, выполнение mvn compile для Maven или gradle build для Gradle приведет к инициации перезапуска.

Если перезапуск осуществляется с помощью Maven или Gradle с использованием плагина сборки, то нужно оставить значение forking, установленное в enabled. Если вы отключите ветвление (форкинг), изолированный загрузчик классов приложения, используемый devtools, не будет создан, а перезагрузка не будет работать должным образом.
Автоматический перезапуск показывает себя очень хорошо при использовании с LiveReload. Если вы используете JRebel, автоматические перезагрузки отключены в пользу динамической перезагрузки классов. Другие функции devtools (такие как LiveReload и переопределение свойств) по-прежнему можно будет использовать.
DevTools используют перехватчик завершения контекста приложения, чтобы закрыть его во время перезапуска. Работа не будет корректной, если перехватчик завершения отключен (SpringApplication.setRegisterShutdownHook(false)).
DevTools необходимо настроить загрузчик ResourceLoader, используемый ApplicationContext. Если ваше приложение уже предусматривает такой загрузчик, то он будет обёрнут. Прямое переопределение метода getResource на ApplicationContext не поддерживается.
Автоматический перезапуск не поддерживается при использовании привязки посредством AspectJ.
Перезапуск и перезагрузка

Технология перезапуска, предусмотренная Spring Boot, работает при помощи двух загрузчиков классов. Классы, которые не изменяются (например, классы из сторонних jar-файлов), загружаются в основной загрузчик классов. Классы, которые активно разрабатываются, загружаются в перезапускающий загрузчик классов. Если приложение перезапускается, перезапускающий загрузчик классов единовременно используется, после чего создается новый. Такой подход означает, что перезапуск приложения обычно происходит гораздо быстрее, чем "холодный запуск", поскольку основной загрузчик классов уже доступен и заполнен.

Если вы обнаружите, что перезагрузка недостаточно быстра для ваших приложений или вы столкнулись с проблемами загрузки классов, то можете ознакомиться с технологиями перезагрузки, такими как JRebel от ZeroTurnaround. Они работают путем переписывания классов по мере их загрузки, чтобы они были более удобными для повторной загрузки.

Журналирование изменений в вычислении состояния

По умолчанию при каждом перезапуске приложения в журнал заносится отчет, показывающий дельту вычисления состояния. Отчет демонстрирует изменения в автоконфигурации вашего приложения по мере того, как вы вносите изменения, такие как добавление или удаление бинов и установка свойств конфигурации.

Чтобы отключить журналирование с формированием отчета, установите следующее свойство:

Properties
spring.devtools.restart.log-condition-evaluation-delta=false
Yaml
spring:
  devtools:
    restart:
      log-condition-evaluation-delta: false

Исключение ресурсов

Некоторые ресурсы не обязательно должны инициировать перезагрузку при их изменении. Например, шаблоны Thymeleaf можно редактировать в том же месте. По умолчанию изменение ресурсов в /META-INF/maven, /META-INF/resources, /resources, /static, /public или /templates не инициирует перезапуск, но инициирует перезагрузку в реальном времени. Если вы хотите настроить эти исключения, то можете использовать свойство spring.devtools.restart.exclude. Например, чтобы исключить только /static и /public, нужно установить следующее свойство:

Properties
spring.devtools.restart.exclude=static/**,public/**
Yaml
spring:
  devtools:
    restart:
      exclude: "static/**,public/**"
Если вы хотите сохранить эти значения по умолчанию и добавить дополнительные исключения, используйте свойство spring.devtools.restart.additional-exclude.

Отслеживание дополнительных путей

Вам может потребоваться, чтобы приложение перезапускалось или перезагружалось после внесения изменений в файлы, которых нет в classpath. Для этого используйте свойство spring.devtools.restart.additional-paths, чтобы сконфигурировать дополнительные пути для отслеживания изменений. Вы можете использовать свойство spring.devtools.restart.exclude, чтобы управлять тем, будут ли изменения в дополнительных путях инициировать полный перезапуск или перезагрузку в реальном времени.

Отключение перезапуска

Если вам не требуется использовать функцию перезапуска, вы можете отключить ее с помощью свойства spring.devtools.restart.enabled. В большинстве случаев можно установить это свойство в файле application.properties (так все равно будет инициализироваться перезапускающий загрузчик классов, но изменения файлов не будут отслеживаться).

Если требуется полностью отключить средства поддержки перезапуска (например, ввиду того, что они не работают с определенной библиотекой), то перед вызовом SpringApplication.run(…​) нужно установить свойство System для spring.devtools.restart.enabled в false,как это показано в следующем примере:

Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        System.setProperty("spring.devtools.restart.enabled", "false");
        SpringApplication.run(MyApplication.class, args);
    }
}
Kotlin
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
@SpringBootApplication
object MyApplication {
    @JvmStatic
    fun main(args: Array<String>) {
        System.setProperty("spring.devtools.restart.enabled", "false")
        SpringApplication.run(MyApplication::class.java, *args)
    }
}

Использование триггерного файла

Если вы работаете с IDE, которая постоянно компилирует изменяемые файлы, то можете предпочесть инициацию перезагрузки только в определенные моменты. Для этого вы можете использовать "триггерный файл", который представляет собой специализированный файл, который необходимо изменить, если вы действительно хотите инициировать проверку перезапуска.

Любое обновление файла вызывает проверку, но перезапуск происходит только в том случае, если Devtools обнаруживают, что им нужно что-то делать.

Чтобы использовать триггерный файл, установите свойство spring.devtools.restart.trigger-file в имя (исключая путь) вашего триггерного файла. Триггерный файл должен находиться где-то в вашем classpath.

Например, если у вас есть проект со следующей структурой:

src
+- main
   +- resources
      +- .reloadtrigger

Тогда свойство вашего trigger-file будет таким:

Properties
spring.devtools.restart.trigger-file=.reloadtrigger
Yaml
spring:
  devtools:
    restart:
      trigger-file: ".reloadtrigger"

Перезапуск теперь будет происходить только при обновлении src/main/resources/.reloadtrigger.

Возможно, вам захочется установить spring.devtools.restart.trigger-file как глобальный параметр, чтобы все ваши проекты имели одинаковую логику работы.

В некоторых IDE существуют функции, которые избавляют от необходимости обновлять триггерный файл вручную. Spring Tools для Eclipse и IntelliJ IDEA (Ultimate Edition) имеют такие средства поодержки. В Spring Tools вы можете использовать кнопку "reload" из представления консоли (при условии, что ваш trigger-file имеет имя .reloadtrigger). В случае с IntelliJ IDEA можно следовать инструкциям в соответствующей документации.

Настройка перезапускающего загрузчика классов

Функциональность перезапуска реализована через два загрузчика классов. Если возникают какие-то проблемы, вам может понадобиться настроить, что и каким загрузчиком классов будет загружаться.

По умолчанию любой открытый проект в вашей IDE загружается с "перезапускающим" загрузчиком классов, а любой обычный .jar-файл загружается с "основным" загрузчиком классов. То же самое верно, если используется mvn spring-boot:run или gradle bootRun: проект, содержащий аннотацию @SpringBootApplication, загружается с "перезапускающим" загрузчиком класса, а все остальное – с "основным" загрузчиком классов.

Можно дать Spring Boot команду загружать компоненты проекта с другим загрузчиком классов, создав файл META-INF/spring-devtools.properties. Файл spring-devtools.properties может содержать свойства с префиксами restart.exclude и restart.include. Элементы include – это компоненты, которые нужно поднять вверх в "перезапускающий" загрузчик классов, а элементы exclude – это элементы, которые нужно опустить вниз в "основной" загрузчик классов. Значение свойства представляет собой шаблон регулярных выражений, который применяется к classpath, как показано в следующем примере:

Properties
restart.exclude.companycommonlibs=/mycorp-common-[\\w\\d-\\.]+\\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w\\d-\\.]+\\.jar
Yaml
restart:
  exclude:
    companycommonlibs: "/mycorp-common-[\\w\\d-\\.]+\\.jar"
  include:
    projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"
Все ключи свойств должны быть уникальными. Если свойство начинается с restart.include. или restart.exclude., то оно учитывается.
Загружаются все META-INF/spring-devtools.properties из classpath. Вы можете упаковывать файлы внутри вашего проекта или в библиотеках, которые использует проект.

Известные ограничения

Функциональность перезапуска не очень хорошо работает с объектами, которые десериализуются с помощью стандартного ObjectInputStream. Если необходимо десериализовать данные, то может понадобиться использовать ConfigurableObjectInputStream из Spring в сочетании с Thread.currentThread().getContextClassLoader().

К сожалению, некоторые сторонние библиотеки десериализуют без учета контекстного загрузчика классов. Если вы столкнетесь с такой проблемой, то необходимо будет запросить исправление у авторов-создателей.

LiveReload

Модуль spring-boot-devtools содержит встроенный сервер LiveReload, который можно использовать для инициации обновления браузера при изменении ресурса. Расширения для браузера LiveReload для Chrome, Firefox и Safari находятся в свободном доступе на сайте livereload.com.

Если запускать сервер LiveReload при выполнении приложения не нужно, то можно установить свойство spring.devtools.livereload.enabled в false.

Одновременно можно запустить только один сервер LiveReload. Перед запуском приложения убедитесь, что не запущены другие серверы LiveReload. Если вы запускаете несколько приложений из IDE, только первое из них будет поддерживать LiveReload.
Для инициации функции LiveReload при изменении файла, автоматический перезапуск должен быть включен.

Глобальные параметры

Вы можете настроить глобальные параметры devtools, добавив любой из следующих файлов в каталог $HOME/.config/spring-boot:

  1. spring-boot-devtools.properties

  2. spring-boot-devtools.yaml

  3. spring-boot-devtools.yml

Любые свойства, добавленные в эти файлы, применяются ко всем приложениям Spring Boot в вашей машине, которые используют devtools. Например, чтобы сконфигурировать перезапуск на постоянное использование триггерного файла, нужно добавить следующее свойство в файл spring-boot-devtools:

Properties
spring.devtools.restart.trigger-file=.reloadtrigger
Yaml
spring:
  devtools:
    restart:
      trigger-file: ".reloadtrigger"

По умолчанию $HOME – это начальный каталог пользователя. Чтобы настроить это местоположение, установите переменную окружения SPRING_DEVTOOLS_HOME или системное свойство spring.devtools.home.

Если конфигурационные файлы devtools не будут найдены в $HOME/.config/spring-boot, в корне каталога $HOME будет выполнен поиск на предмет наличия файла .spring-boot-devtools.properties. Это позволит совместно использовать глобальную конфигурацию devtools с приложениями, которые работают на более старой версии Spring Boot, не поддерживающей местоположение $HOME/.config/spring-boot.

Профили не поддерживаются в файлах properties/yaml для devtools.

Любые профили, активированные в .spring-boot-devtools.properties, не будут влиять на загрузку связанных с конкретным профилем файлов. Имена файлов, специфичные для профилей (вида spring-boot-devtools-<profile>.properties), и документы spring.config.activate.on-profile в файлах YAML и Properties не поддерживаются.

Конфигурирование watcher-а файловой системы

FileSystemWatcher работает путем опроса изменений класса в определенных интервалах времени, а затем ожидает предопределенного периода бездействия, чтобы удостовериться, что изменений больше нет. Поскольку Spring Boot всецело полагается на IDE при компиляции и копировании файлов в место, откуда Spring Boot может их прочитать, вы можете столкнуться с тем, что некоторые изменения не будут отражены при перезапуске приложения с помощью devtools. Если вы наблюдаете подобные проблемы постоянно, попробуйте увеличить параметры spring.devtools.restart.poll-interval и spring.devtools.restart.quiet-period до значений, соответствующих вашему окружению разработки:

Properties
spring.devtools.restart.poll-interval=2s
spring.devtools.restart.quiet-period=1s
Yaml
spring:
  devtools:
    restart:
      poll-interval: "2s"
      quiet-period: "1s"

Теперь отслеживаемые каталоги classpath опрашиваются каждые 2 секунды на предмет изменений, а 1-секундный период бездействия соблюдается, чтобы удостовериться в отсутствии дополнительных изменений классов.

Удаленные приложения

Инструменты разработчика Spring Boot не ограничиваются локальной разработкой. Вы также можете использовать несколько функций при удаленном запуске приложений. Средства поддержки удалённой работы принимаются по желанию, поскольку их активация может представлять угрозу безопасности. Их следует активировать только при работе в доверенной сети или при наличии защиты с помощью SSL. Если ни один из этих вариантов не доступен, не следует использовать средства поддержки удалённой работы для DevTools. Ни в коем случае не следует активировать средства поддержки при производственном развертывании.

Чтобы активировать поддержку, необходимо убедиться, что devtools содержится в переупакованном архиве, как показано в следующем листинге:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludeDevtools>false</excludeDevtools>
            </configuration>
        </plugin>
    </plugins>
</build>

Затем необходимо установить свойство spring.devtools.remote.secret. Как и любой важный пароль или конфиденциальные сведения, значение должно быть уникальным и надежным, чтобы его нельзя было угадать или подобрать методом перебора.

Средства поддержки удалённой работы для devtools предоставляются в двух частях: конечная точка на стороне сервера, принимающая соединения, и клиентское приложение, которое вы выполняете в своей IDE. Серверный компонент автоматически активируется, если установлено свойство spring.devtools.remote.secret. Клиентский компонент нужно запускать вручную.

Удаленная работа с devtools не поддерживается для приложений Spring WebFlux.

Выполнение удаленного клиентского приложения

Удаленное клиентское приложение разработано для выполнения из вашей IDE. Необходимо выполнить org.springframework.boot.devtools.RemoteSpringApplication с тем же classpath, что и удаленный проект, к которому вы подключаетесь. Единственным обязательным аргументом приложения является удаленный URL-адрес, к которому оно подключается.

Например, если используется Eclipse или Spring Tools и имеется проект с именем my-app, который развернут в Cloud Foundry, нужно сделать следующее:

  • Выбрать Run Configurations…​ из меню Run.

  • Создать новую "конфигурацию запуска" Java Application.

  • Найти проект my-app.

  • Использовать org.springframework.boot.devtools.RemoteSpringApplication в качестве основного класса.

  • Добавить https://myapp.cfapps.io в Program arguments (или любой другой URL-адрес вашего удаленного доступа).

Выполняемый удаленный клиент может выглядеть следующим образом:

  .   ____          _                                              __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _          ___               _      \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |        | _ \___ _ __  ___| |_ ___ \ \ \ \
 \\/  ___)| |_)| | | | | || (_| []::::::[]   / -_) '  \/ _ \  _/ -_) ) ) ) )
  '  |____| .__|_| |_|_| |_\__, |        |_|_\___|_|_|_\___/\__\___|/ / / /
 =========|_|==============|___/===================================/_/_/_/
 :: Spring Boot Remote ::  (v2.7.5)
2022-10-20 12:40:15.175  INFO 16215 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication v2.7.5 using Java 1.8.0_345 on myhost with PID 16215 (/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-devtools/2.7.5/spring-boot-devtools-2.7.5.jar started by myuser in /opt/apps/)
2022-10-20 12:40:15.182  INFO 16215 --- [           main] o.s.b.devtools.RemoteSpringApplication   : No active profile set, falling back to 1 default profile: "default"
2022-10-20 12:40:15.913  INFO 16215 --- [           main] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2022-10-20 12:40:15.946  INFO 16215 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 1.471 seconds (JVM running for 2.063)
Поскольку удаленный клиент использует тот же classpath, что и фактическое приложение, он может напрямую читать свойства приложения. Так считывается свойство spring.devtools.remote.secret и передается серверу для аутентификации.
Всегда рекомендуется использовать https:// в качестве протокола подключения, чтобы трафик был зашифрован и пароли нельзя было перехватить.
Если необходимо использовать прокси для доступа к удаленному приложению, сконфигурируйте свойства spring.devtools.remote.proxy.host и spring.devtools.remote.proxy.port.

Удаленное обновление

Удаленный клиент отслеживает изменения в пути классов вашего приложения так же, как это делает программа локального перезапуска. Любой обновленный ресурс передается удаленному приложению и (если требуется) инициирует перезапуск. Это может быть полезно, если вы повторяете функцию, использующую облачную службу, которой нет на локальном уровне. Как правило, удаленное обновление и перезагрузка происходят гораздо быстрее, чем полная перестройка и развертывание.

В окружении с медленной разработкой может случиться так, что периода бездействия будет недостаточно, а изменения в классах могут быть разбиты на пакеты. Сервер будет перезапускаться после загрузки первого пакета изменений класса. Следующий пакет нельзя будет отправить в приложение, так как сервер перезапускается.

Обычно это проявляется в виде предупреждения в журналах RemoteSpringApplication о том, что не удалось загрузить некоторые классы, и последующей повторной попытки. Но это также может вылиться в несогласованность кода приложения и невозможность перезапуска после загрузки первого пакета изменений. Если вы наблюдаете подобные проблемы постоянно, попробуйте увеличить параметры spring.devtools.restart.poll-interval и spring.devtools.restart.quiet-period до значений, соответствующих вашему окружению разработки.

Файлы отслеживаются только во время выполнения удаленного клиента. Если вы измените файл до запуска удаленного клиента, он не будет перенесен на удаленный сервер.