Вам нужно запускать процесс каждый день в одно и то же время, как будильник?  Тогда запланированные задачи 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 
 | 
@Componentpublic 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 
 | 
@Componentpublic 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@EnableSchedulingpublic 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, являются их собственными.  |