Вам нужно запускать процесс каждый день в одно и то же время, как будильник? Тогда запланированные задачи Spring для вас. Позволяет вам аннотировать метод с помощью @Scheduled
заставляя его запускаться в определенное время или через определенный промежуток времени. В этом посте мы рассмотрим настройку проекта, который может использовать запланированные задачи, а также как использовать различные методы для определения того, когда они выполняются.
Я буду использовать Spring Boot для этого поста, чтобы сделать зависимости красивыми и простыми из-за того, что планирование доступно для зависимости spring-boot-starter
которая будет в некотором роде включена в каждый проект Spring Boot. Это позволяет вам использовать любые другие зависимости для стартера, так как они будут задействовать spring-boot-starter
и все его отношения. Если вы хотите включить саму точную зависимость, используйте spring-context
.
Вы можете использовать spring-boot-starter
.
1
2
3
4
5
|
< dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter</ artifactId > < version >2.0.0.RC1</ version > </ dependency > |
Или используйте spring-context
напрямую.
1
2
3
4
5
|
< dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context</ artifactId > < version >5.0.3.RELEASE</ version > </ dependency > |
Создать запланированное задание довольно просто. Добавьте аннотацию @Scheduled
для любого метода, который вы хотите запустить автоматически, и @EnableScheduling
в файл конфигурации.
Так, например, вы можете иметь что-то вроде ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
@Component public class EventCreator { private static final Logger LOG = LoggerFactory.getLogger(EventCreator. class ); private final EventRepository eventRepository; public EventCreator( final EventRepository eventRepository) { this .eventRepository = eventRepository; } @Scheduled (fixedRate = 1000 ) public void create() { final LocalDateTime start = LocalDateTime.now(); eventRepository.save( new Event( new EventKey( "An event type" , start, UUID.randomUUID()), Math.random() * 1000 )); LOG.debug( "Event created!" ); } } |
Здесь довольно много кода, который не имеет значения для запуска запланированного задания. Как я сказал минуту назад, нам нужно использовать @Scheduled
для метода, и он запустится автоматически. Таким образом, в приведенном выше примере метод create
будет запускаться каждые 1000 мс (1 секунда), как fixedRate
свойстве fixedRate
аннотации. Если мы хотим изменить частоту его выполнения, мы можем увеличить или уменьшить время fixedRate
или рассмотреть возможность использования различных доступных нам методов планирования.
Таким образом, вы, вероятно, хотите знать, каковы эти другие способы? Ну, вот они (я также fixedRate
здесь fixedRate
).
-
fixedRate
выполняет метод с фиксированным периодом в миллисекундах между вызовами. -
fixedRateString
такой же, какfixedRate
но со строковым значением. -
fixedDelay
выполняет метод с фиксированным периодом в миллисекунды между концом одного вызова и началом следующего. -
fixedDelayString
такой же, какfixedDelay
но со строковым значением. -
cron
использует cron-подобные выражения, чтобы определить, когда выполнять метод (мы рассмотрим это более подробно позже).
Есть несколько других служебных свойств, доступных для аннотации @Scheduled
.
-
zone
Указывает часовой пояс, для которого будет разрешено выражение cron. Если часовой пояс не указан, будет использоваться часовой пояс сервера по умолчанию. Поэтому, если вам нужно, чтобы он работал в определенном часовом поясе, например, в Гонконге, вы можете использоватьzone = "GMT+8:00"
. -
initialDelay
Количество миллисекунд, чтобы задержать первое выполнение запланированной задачи, требует использования одного из свойств фиксированной скорости или фиксированной задержки. -
initialDelayString
То же, чтоinitialDelay
но со строковым значением.
Несколько примеров использования фиксированных ставок и задержек можно найти ниже.
1
|
@Scheduled (fixedRate = 1000 ) |
То же, что и раньше, запускается каждую 1 секунду.
1
|
@Scheduled (fixedRateString = "1000" ) |
То же, что и выше.
1
|
@Scheduled (fixedDelay = 1000 ) |
Запускается через 1 секунду после завершения предыдущего вызова.
1
|
@Scheduled (fixedRate = 1000 , initialDelay = 5000 ) |
Запускается каждую секунду, но ждет 5 секунд, прежде чем выполнится в первый раз.
Теперь рассмотрим свойство cron
которое дает гораздо больший контроль над расписанием задачи, позволяя нам определить секунды, минуты и часы, в которые выполняется задача, но может пойти еще дальше и указать даже годы, в которые задача будет выполняться.
Ниже приведена разбивка компонентов, которые создают выражение cron.
-
Seconds
могут иметь значения0-59
или специальные символы, - * /
. -
Minutes
могут иметь значения0-59
или специальные символы, - * /
. -
Hours
могут иметь значения0-59
или специальные символы, - * /
. -
Day of month
может иметь значения1-31
или специальные символы, - * ? / LWC
, - * ? / LWC
. -
Month
может иметь значения1-12
,JAN-DEC
или специальные символы, - * /
. -
Day of week
может иметь значения1-7
,SUN-SAT
или специальные символы, - * ? / LC #
, - * ? / LC #
. -
Year
может быть пустым, иметь значения1970-2099
или специальные символы, - * /
.
Просто для большей ясности я объединил разбивку в выражение, состоящее из меток полей.
1
|
@Scheduled (cron = "[Seconds] [Minutes] [Hours] [Day of month] [Month] [Day of week] [Year]" ) |
Пожалуйста, не включайте фигурные скобки в свои выражения (я использовал их, чтобы сделать выражение более понятным).
Прежде чем мы продолжим, нам нужно разобраться, что означают специальные символы.
-
*
представляет все значения, поэтому, если используется во втором поле, оно означает каждую секунду или используется в поле дня, означая запускать каждый день. -
?
не представляет никакого конкретного значения и может использоваться в поле дня месяца или дня недели, где использование одного делает недействительным другое. Если мы укажем срабатывание 15 числа месяца, то?
будет использоваться в поле «Day of week
. -
-
представляет инклюзивный диапазон значений, например 1-3 в поле часов означает часы 1, 2 и 3. -
,
представляет дополнительные значения, например, MON, WED, SUN в полях дня недели, означает понедельник, среду и воскресенье. -
/
представляет приращения, например, 0/15 в поле секунд запускается каждые 15 секунд, начиная с 0 (0, 15, 30 и 45). -
L
представляет последний день недели или месяца. Помните, что в этом контексте суббота является концом недели, поэтому использованиеL
в поле дня недели вызовет субботу. Это может использоваться в сочетании с числом в поле дня месяца, таким как6L
для представления последней пятницы месяца или выражением типаL-3
обозначающим третий с последнего дня месяца. Если мы указываем значение в поле дня недели, которое мы должны использовать?
в поле дня месяца, и наоборот. -
W
представляет ближайший день недели месяца. Например, если15W
сработает в 15-й день месяца, если это будний день, в противном случае он будет работать в ближайший день недели. Это значение нельзя использовать в списке значений дня. -
#
задает как день недели, так и неделю, когда должно запускаться задание. Например,5#2
означает второй четверг месяца. Если указанные вами день и неделя переполнятся на следующий месяц, они не сработают.
Здесь можно найти полезный ресурс с чуть более длинными пояснениями, который помог мне написать этот пост.
Давайте рассмотрим несколько примеров.
1
|
@Scheduled (cron = "0 0 12 * * ?" ) |
Пожары в 12 вечера каждый день.
1
|
@Scheduled (cron = "0 15 10 * * ? 2005" ) |
Пожары в 10:15 каждый день в 2005 году.
1
|
@Scheduled (cron = "0/20 * * * * ?" ) |
Срабатывает каждые 20 секунд.
Для некоторых других примеров см. Ссылку, которую я упоминал ранее, показанную здесь К счастью, если вы застряли при написании простого выражения cron, вы сможете найти нужный вам сценарий, поскольку кто-то, возможно, уже задавал тот же вопрос о переполнении стека.
Чтобы связать некоторые из вышеперечисленных в небольшой пример кода, см. Код ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Component public class AverageMonitor { private static final Logger LOG = LoggerFactory.getLogger(AverageMonitor. class ); private final EventRepository eventRepository; private final AverageRepository averageRepository; public AverageMonitor( final EventRepository eventRepository, final AverageRepository averageRepository) { this .eventRepository = eventRepository; this .averageRepository = averageRepository; } @Scheduled (cron = "0/20 * * * * ?" ) public void publish() { final double average = eventRepository.getAverageValueGreaterThanStartTime( "An event type" , LocalDateTime.now().minusSeconds( 20 )); averageRepository.save( new Average( new AverageKey( "An event type" , LocalDateTime.now()), average)); LOG.info( "Average value is {}" , average); } } |
Здесь у нас есть класс, который запрашивает Cassandra каждые 20 секунд для среднего значения событий за тот же период времени. Опять же, большая часть кода здесь является шумом от аннотации @Scheduled
но может быть полезно увидеть ее в дикой природе. Более того, если вы были внимательны, для этого fixedRate
использования запуска каждые 20 секунд было бы целесообразно использовать fixedRate
и, возможно, свойства fixedDelay
вместо cron
, поскольку мы выполняем задачу так часто.
1
|
@Scheduled (fixedRate = 20000 ) |
Является ли fixedRate
эквивалентом выражения cron, использованного выше.
Последнее требование, о котором я упоминал ранее, — добавить аннотацию @EnableScheduling
в класс конфигурации.
1
2
3
4
5
6
7
8
|
@SpringBootApplication @EnableScheduling public class Application { public static void main( final String args[]) { SpringApplication.run(Application. class ); } } |
Поскольку это небольшое приложение Spring Boot, я прикрепил аннотацию @EnableScheduling
к основному классу @SpringBootApplication
.
В заключение, мы можем запланировать запуск задач с помощью аннотации @Scheduled
а также либо с @Scheduled
до миллисекунды между выполнениями, либо с помощью выражения cron для более точного времени, которое нельзя выразить первым. Для задач, которые нужно запускать очень часто, достаточно использовать свойства fixedRate
или fixedDelay
, но как только время между выполнениями станет больше, будет сложнее быстро определить определенное время. Когда это происходит, следует использовать свойство cron
для большей ясности запланированного времени.
Небольшое количество кода, используемого в этом посте, можно найти на моем GitHub .
Если вы сочли этот пост полезным и хотите быть в курсе моих новых учебных пособий по мере их написания, следите за мной в Twitter по адресу @LankyDanDev .
Опубликовано на Java Code Geeks с разрешения Дэна Ньютона, партнера нашей программы JCG . См. Оригинальную статью здесь: Запуск в срок с запланированными задачами Spring
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |