[Перевод] 6 способов выполнения метода при старте Spring Boot приложения

4f29d39f7f060ccd0046e3fc688d679c

При разработке на Spring Boot иногда нам нужно выполнить метод или фрагмент кода при запуске приложения. Этот код может быть любым, от записи определенной информации до настройки базы данных, заданий cron и т. д. Мы не можем просто поместить этот код в конструктор, потому что требуемые переменные или службы могут быть еще не инициализированы. Это может привести к null pointer исключению или некоторым другим.

Зачем нам нужно запускать код при запуске Spring Boot?

Нам нужно запускать метод при запуске приложения по многим причинам, например, для:

  • Записи важных событий или сообщения о запуске приложения

  • Обработки базы данных или файлов, индексации, создания кэшей и т. д.

  • Запуска фоновых процессов, таких как отправка уведомления, выборка данных из некоторой очереди и т. д.

Различные способы запуска метода после запуска Spring Boot

У каждого способа есть свои преимущества. Давайте рассмотрим подробнее, чтобы решить, что нам следует использовать:

  1. интерфейс CommandLineRunner

  2. интерфейс ApplicationRunner

  3. события Spring Boot Application

  4. аннотацию @Postconstruct для метода

  5. интерфейс InitializingBean

  6. атрибут инициализации аннотации @bean

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

CommandLineRunner — это функциональный интерфейс Spring Boot, который используется для запуска кода при запуске приложения. Он находится в пакете org.springframework.boot.

В процессе запуска после инициализации контекста Spring boot вызывает свой метод run () с аргументами командной строки, предоставленными приложению.

Чтобы сообщить Spring Boot о нашем интерфейсе commandlineRunner, мы можем либо реализовать его и добавить аннотацию @Component над классом, либо создать его bean-компонент с помощью @bean.

Пример реализации интерфейса CommandLineRunner

@Component
public class CommandLineRunnerImpl implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("In CommandLineRunnerImpl ");

        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

Пример создания bean-компонента интерфейса CommandLineRunner

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

    @Bean
    public CommandLineRunner CommandLineRunnerBean() {
        return (args) -> {
            System.out.println("In CommandLineRunnerImpl ");

            for (String arg : args) {
                System.out.println(arg);
            }
        };
    }
}

Мы можем запустить приложение из командной строки или IDE. Давайте рассмотрим пример, когда мы запускаем приложение с аргументами »–status = running».

mvn spring-boot:run -Dspring-boot.run.arguments="--status=running"

ИЛИ 

mvn package 
java -jar target/ --status=running

Это дает следующий вывод журнала:

In CommandLineRunnerImpl
status=running

Как мы видим, параметр не анализируется, а вместо этого интерпретируется как одно значение «status = running».

Чтобы получить доступ к аргументам командной строки в проанализированном формате, нам нужно использовать интерфейс ApplicationRunner. Мы рассмотрим это чуть позже.

Spring Boot добавляет интерфейс CommandLineRunner в процесс запуска. Следовательно, выброшенное исключение в commandlineRunner заставит процесс загрузки Spring прервать запуск.

Мы можем создать несколько CommandLineRunner в одном приложении. Используя интерфейс Ordered или аннотацию @Order, мы можем настроить порядок, в котором они должны запускаться. Меньшее значение означает более высокий приоритет. По умолчанию все компоненты создаются с самым низким приоритетом. Поэтому компоненты не указанные конфигурации order будут вызываться последними.

Мы можем использовать аннотацию @Order, как показано ниже

@Component
@Order(1)
public class CommandLineRunnerImpl implements CommandLineRunner {
    ........
}

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

Как обсуждалось ранее, для доступа к анализируемым аргументам нам необходимо использовать интерфейс ApplicationRunner. Интерфейс ApplicationRunner предоставляет метод запуска с ApplicationArguments вместо необработанного массива строк.

ApplicationArguments — это интерфейс, который доступен из srping boot 1.3 в пакете org.springframework.boot.

Он предоставляет различные способы доступа к аргументам, как показано ниже.

String[] GetSourceArgs ()

Предоставляет необработанные аргументы, которые были переданы приложению

Set getOptionNames ()

Именам всех необязательных аргументов предшествует »-», например: –name = «stacktrace»

List getNonOptionArgs ()

Возвращает необработанные необязательные аргументы. Аргументы без »-»

boolean containsOption (String name)

Проверяет, присутствует ли имя в необязательных аргументах или нет

List getOptionValues (String name)

Предоставляет значение аргумента по имени

Метод getOptionValues ​​возвращает список значений, потому что значение аргумента может быть массивом, поскольку мы можем использовать один и тот же ключ более одного раза в командной строке.

Например, –name = «stacktrace» — Port = 8080 –name = «guru».

Пример реализации интерфейса Application runner

Давайте запустим приведенную ниже программу, используя аргументы «status = running –mood = happy 10–20», и давайте разберемся с результатом.

@Component
public class ApplicationRunnerImpl implements ApplicationRunner {

   @Override
   public void run(ApplicationArguments args) throws Exception {

      System.out.println("ApplicationRunnerImpl Called");

//print all arguemnts: arg: status=running, arg: --mood=happy, 10, --20
      for (String arg : args.getSourceArgs()) {
         System.out.println("arg: "+arg);
      }
      System.out.println("NonOptionArgs: "+args.getNonOptionArgs()); //[status=running,10]
      System.out.println("OptionNames: "+args.getOptionNames());  //[mood, 20]

     System.out.println("Printing key and value in loop:");
      for (String key : args.getOptionNames()) {
         System.out.println("key: "+key);     //key: mood  //key: 20
         System.out.println("val: "+args.getOptionValues(key)); //val:[happy] //val:[]
      }
   }
}

Вывод:

ApplicationRunnerImpl Called
arg: status=running
arg: --mood=happ
arg: 10
arg: --20
NonOptionArgs: [status=running , 10]
OptionNames: [mood, 20]
Printing key and value in loop:
key: mood
val: [happy]
key: 20
val: []

CommandLineRunner и ApplicationRunner имеют схожие функции, например:

  • Исключение в методе run () прервет запуск приложения.

  • Несколько ApplicationRunner можно заказать с помощью упорядоченного интерфейса или аннотации @Order.

Важнее всего отметить, что порядок разделяется между CommandLineRunners и ApplicationRunners. Это означает, что порядок выполнения может быть смешанным между commandlineRunner и applicationRunner.

3. Событие приложения в Spring Boot

Фреймворк Spring запускает разные события в разных ситуациях. Он также вызывает множество событий в процессе запуска. Мы можем использовать эти события для выполнения нашего кода, например, ApplicationReadyEvent можно использовать для выполнения кода после запуска приложения загрузки Spring.

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

@Component
public class RunAfterStartup{

@EventListener(ApplicationReadyEvent.class)
public void runAfterStartup() {
    System.out.println("Yaaah, I am running........");
}

Некоторые наиболее важные события spring boot,

  • ApplicationContextInitializedEvent: запускается после подготовки ApplicationContext и вызова ApplicationContextInitializers, но до загрузки определений bean-компонентов

  • ApplicationPreparedEvent: запускается после загрузки определений bean-компонентов

  • ApplicationStartedEvent: запускается после обновления контекста, но до вызова командной строки и запуска приложений.

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

  • ApplicationFailedEvent: срабатывает, если есть исключение при запуске

Можно создать несколько ApplicationListeners. Их можно упорядочивать с помощью аннотации @Order или с помощью интерфейса Ordered.

Order используется совместно с другими ApplicationListeners того же типа, но не с ApplicationRunners или CommandLineRunners.

4. Аннотация метода @Postconstruct

Метод можно пометить аннотацией @PostConstruct. Всякий раз, когда метод отмечен этой аннотацией, он будет вызываться сразу после внедрения зависимости.

Метод @PostConstruct связан с конкретным классом, поэтому его следует использовать только для кода конкретного класса. В каждом классе может быть только один метод с аннотацией postConstruct.

@Component
public class PostContructImpl {

	public PostContructImpl() {
		System.out.println("PostContructImpl Constructor called");
	}

	@PostConstruct
	public void runAfterObjectCreated() {
		System.out.println("PostContruct method called");
	}
}

Вывод:

PostContructImpl Constructor called
postContruct method called

Следует отметить, что, если класс помечен как lazy, это означает, что класс создается по запросу. После этого будет выполнен метод, помеченный аннотацией @postConstruct.

Метод, отмеченный аннотацией postConstruct, может иметь любое имя, но не должен иметь никаких параметров. Он должен быть void и не должен быть static.

Обратите внимание, что аннотация @postConstruct является частью модуля Java EE и помечена как устаревшая в Java 9 и удалена в Java 11. Мы все еще можем использовать ее, добавив java.se.ee в приложение.

5. Интерфейс InitializingBean

Решение InitializingBean работает точно так же, как аннотация postConstruct. Вместо использования аннотации мы должны реализовать интерфейс InitializingBean. Затем нам нужно переопределить метод void afterPropertiesSet ().

InitializingBean является частью пакета org.springframework.beans.factory.

@Component
public class InitializingBeanImpl implements InitializingBean {
    public InitializingBeanImpl() {
        System.out.println("InitializingBeanImpl Constructor called");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBeanImpl afterPropertiesSet method called");
    }
}

Вы можете подумать, что же произойдет, если мы будем использовать одновременно аннотацию @PostConstruct и InitializingBean. В этом случае метод @PostConstruct будет вызываться перед методом afterPropertiesSet () InitializingBean.

6. Атрибут Init аннотации @bean

Мы можем реализовать метод, используя свойство initMethod в аннотации @Bean. Этот метод будет вызываться после инициализации bean-компонента.

Метод, предоставленный в initMethod, должен быть void и не иметь аргументов. Этот метод может быть даже private.

public class BeanInitMethodImpl {
 
    public void runAfterObjectCreated() {
        System.out.println("yooooooooo......... someone called me");
    }
} 

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean(initMethod="runAfterObjectCreated")
    public BeanInitMethodImpl getFunnyBean() {
        return new BeanInitMethodImpl();
    }
 }

Вывод:

yooooooooo......... someone called me

Если у вас есть реализация InitializingBean и свойство initMethod аннотации @Bean для того же класса, то метод afterPropertiesSet для InitializingBean будет вызываться перед initMethod.

Сочетание разных подходов

Наконец, иногда нам может потребоваться объединить несколько вариантов. При этом они будут выполняться в следующем порядке:

  • Конструктор

  • PostContruct метод

  • afterPropertiesSet метод

  • Метод инициализации Bean-компонента

  • ApplicationStartedEvent

  • ApplicationRunner или CommandLineRunner в зависимости от порядка

  • ApplicationReadyEvent

Краткий обзор

  • Есть разные способы запустить код после запуска приложения с весенней загрузкой

  • Мы можем использовать CommandLineRunner или ApplicationRunner Interface.

  • Используйте интерфейс ApplicationRunner для доступа к анализируемым аргументам вместо массива необработанных строк

  • Событие загрузки Spring выполняет код при запуске приложения

  • Метод, отмеченный аннотацией @PostConstruct, выполняется после инициализации объекта

  • Метод afterPropertiesSet () интерфейса InitializingBean вызывается после инициализации объекта.

  • В аннотации @Bean есть атрибут «initMethod» для предоставления метода, который будет вызываться после инициализации bean-компонента.

Связанные темы

© Habrahabr.ru