Аннотация @Scheduled предлагает простой способ создания запланированных задач в приложениях на базе Spring. Мы можем использовать его для планирования наших задач, используя либо периодическое планирование, либо выражения cron.
Хотя планирование периодов также может быть полезным, выражения cron дают нам гораздо больший контроль над вызовом запланированных задач. Вот почему они очень полезны в реальных приложениях. Однако использование выражений cron имеет один существенный недостаток, если оно сделано неправильно. Давайте выясним, что это такое.
Создание запланированной задачи
Давайте предположим, что мы хотим создать задачу, которая вызывается один раз в секунду и которая просто записывает сообщение в журнал. Мы можем создать эту задачу, выполнив следующие действия (мы пропустим необходимую конфигурацию, поскольку она описана во второй части этого поста):
- Создайте класс с именем ScheduledJob .
- Аннотируйте класс с помощью аннотации @Component .
- Создайте личное поле Logger и создайте его экземпляр.
- Создайте открытый метод run () и убедитесь, что его тип возвращаемого значения void .
- Аннотируйте метод с помощью аннотации @Scheduled и установите используемое выражение cron в качестве значения атрибута cron ( Планировщик Cron в Spring предоставляет хороший обзор выражений cron).
- Реализуйте метод, записав одно сообщение в журнал.
Исходный код класса ScheduledJob выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ScheduledJob { private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob. class ); @Scheduled (cron = "0-59 * * * * *" ) public void run() { LOGGER.debug( "run()" ); } } |
Проблема нашей реализации в том, что выражение cron жестко закодировано. Это означает, что невозможно использовать разные конфигурации в разных средах.
Если мы хотим использовать разные конфигурации планирования в разных средах, мы должны изменить конфигурацию вручную, прежде чем создавать развернутый двоичный файл.
Это естественно подвержено ошибкам. Поскольку последствия использования неправильной конфигурации планирования могут быть серьезными, мы должны найти способ перенести наши выражения cron из кода в файлы конфигурации нашего проекта.
Перемещение выражений Cron в файл свойств
Когда я искал решение нашей проблемы, я наткнулся на эту ветку . Решение, описанное в этом блоге, основано на этом обсуждении.
Требования нашего решения следующие:
- Он должен иметь разные конфигурации для среды производства и разработки.
- Когда запланированное задание запускается в среде разработки, оно должно вызываться один раз в секунду.
- Когда запланированное задание запускается в производственной среде, оно должно вызываться раз в минуту.
Мы можем выполнить эти требования, выполнив следующие действия:
- Настроить Maven.
- Создайте файлы свойств.
- Настройте контекст приложения.
- Изменить класс задачи.
Давайте начнем.
Настройка Maven
Мы можем настроить Maven, выполнив следующие действия:
- Создание профилей для среды разработки и производства.
- Настройте фильтрацию ресурсов.
Давайте двигаться дальше и узнаем, как это делается.
Создание профилей для среды разработки и производства
Как мы помним, нам нужно создавать профили Maven для среды разработки и производства.
Мы можем создать профиль, используемый в среде разработки, выполнив следующие действия:
- Добавьте новый профиль в раздел профилей файла POM.
- Установите идентификатор созданного профиля в «dev».
- Убедитесь, что профиль разработки активен по умолчанию.
- Создайте свойство с именем build.profile.id и установите для него значение «dev».
Мы можем создать производственный профиль, выполнив следующие действия:
- Добавьте новый профиль в раздел профилей файла POM.
- Установите идентификатор созданного профиля на «Prod».
- Создайте свойство с именем build.profile.id и установите для него значение «prod».
Раздел профилей нашего файла pom.xml выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
< profiles > < profile > < id >dev</ id > < activation > < activeByDefault >true</ activeByDefault > </ activation > < properties > < build.profile.id >dev</ build.profile.id > </ properties > </ profile > < profile > < id >prod</ id > < properties > < build.profile.id >prod</ build.profile.id > </ properties > </ profile > </ profiles > |
Мы будем использовать свойство build.profile.id при настройке фильтрации ресурсов нашей сборки. Посмотрим, как это делается.
Настройка фильтрации ресурсов
Мы можем настроить фильтрацию ресурсов , выполнив следующие действия:
- Настройте расположение файла конфигурации, который содержит специфические свойства профиля (значение свойства build.profile.id определяет используемый профиль).
- Настройте расположение каталога ресурсов и активируйте фильтрацию ресурсов.
Соответствующая часть нашего файла pom.xml выглядит следующим образом:
1
2
3
4
5
6
7
8
9
|
< filters > < filter >profiles/${build.profile.id}/config.properties</ filter > </ filters > < resources > < resource > < filtering >true</ filtering > < directory >src/main/resources</ directory > </ resource > </ resources > |
Создание файлов свойств
Мы можем создать необходимые файлы свойств, выполнив следующие действия:
- Мы должны создать файл свойств для среды разработки.
- Мы должны создать файл свойств для производственной среды.
- Мы должны создать файл свойств, который будет прочитан нашим приложением.
Давайте начнем.
Создание файла свойств для среды разработки
Мы можем создать файл свойств для среды разработки, выполнив следующие действия:
- Создайте файл с именем config.properties в каталоге profile / dev .
- Установите значение свойства scheduling.job.cron равным 0-59 * * * * *. Это гарантирует, что задача вызывается один раз в секунду.
Содержимое файла profile / dev / config.properties выглядит следующим образом:
1
|
scheduling.job. cron =0-59 * * * * * |
Создание файла свойств для производственной среды
Мы можем создать файл свойств для производственной среды, выполнив следующие действия:
- Создайте файл с именем config.properties в каталоге profile / prod .
- Установите для свойства scheduling.job.cron значение 0 0-59 * * * *. Это гарантирует, что задача вызывается раз в минуту.
Содержимое файла profile / prod / config.properties выглядит следующим образом:
1
|
scheduling.job. cron =0 0-59 * * * * |
Создание файла свойств нашего приложения
Мы можем создать файл свойств нашего приложения, выполнив следующие действия:
- Создайте файл с именем application.properties в каталоге src / main / resources .
- Установите для свойства scheduling.job.cron значение «$ {scheduling.job.cron}». Это гарантирует, что заполнитель будет заменен на правильное выражение cron.
Содержимое файла src / main / resources / application.properties выглядит следующим образом:
1
|
scheduling.job. cron =${scheduling.job. cron } |
Настройка контекста приложения
Мы можем настроить контекст приложения нашего приложения, используя либо класс конфигурации Java, либо файл конфигурации XML.
Обе эти опции описаны ниже.
Конфигурация Java
Мы можем создать класс конфигурации контекста приложения, выполнив следующие действия:
- Создайте класс с именем ExampleApplicationContext .
- Аннотируйте класс с помощью аннотации @Configuration .
- Включите планирование, аннотируя класс аннотацией @EnableScheduling .
- Пометьте класс аннотацией @ComponentScan и настройте отсканированные пакеты.
- Пометьте класс аннотацией @PropertySource и убедитесь, что свойства загружены из файла свойств с именем application.properties, который находится в пути к классам.
- Создайте новый объект PropertySourcesPlaceHolderConfigurer .
Исходный код нашего класса конфигурации контекста приложения выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import org.springframework.context.annotation.*; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.annotation.EnableScheduling; @Configuration @EnableScheduling @ComponentScan (basePackages = { "net.petrikainulainen.spring.trenches.scheduling" }) @PropertySource ( "classpath:application.properties" ) public class ExampleApplicationContext { @Bean public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer(); properties.setLocation( new ClassPathResource( "application.properties" )); properties.setIgnoreResourceNotFound( false ); return properties; } } |
Конфигурация XML
Мы можем создать файл конфигурации контекста приложения, выполнив следующие действия:
- Используйте элемент property-placeholder пространства имен контекста для загрузки свойств из файла свойств с именем application.properties, который находится в classpath.
- Используйте элемент annotation-config пространства имен контекста, чтобы гарантировать, что «общие» аннотации обнаруживаются из наших классов bean-компонентов.
- Используйте элемент component-scan пространства имен контекста для настройки отсканированных пакетов.
- Включите планирование, используя элемент, управляемый аннотациями, в пространстве имен задачи .
Исходный код нашего файла конфигурации контекста приложения выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd"> < context:property-placeholder location = "classpath:application.properties" ignore-resource-not-found = "false" /> < context:annotation-config /> < context:component-scan base-package = "net.petrikainulainen.spring.trenches.scheduling" /> < task:annotation-driven /> </ beans > |
Изменение запланированной задачи
Наш последний шаг — изменить класс задачи и убедиться, что используемое выражение cron читается из файла application.properties . Мы можем сделать это, установив значение атрибута cron аннотации @Scheduled равным «$ {scheduling.job.cron}».
Исходный код класса ScheduledJob выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ScheduledJob { private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob. class ); @Scheduled (cron = "${scheduling.job.cron}" ) public void run() { LOGGER.debug( "run()" ); } } |
Резюме
Теперь мы создали запланированное задание, которое считывает использованное выражение cron из файла свойств. Этот пост научил нас трем вещам:
- Мы узнали, что жесткое кодирование используемого выражения cron затрудняет использование разных конфигураций в разных средах.
- Мы узнали, как мы можем использовать Maven для разделения свойств конфигурации конкретного профиля на файлы конфигурации конкретного профиля.
- Мы научились настраивать контекст приложения нашего приложения и читать используемое выражение cron из файла свойств.
Как всегда, пример приложения этого поста доступен на Github .