При разработке на Spring Boot иногда нам нужно выполнить метод или фрагмент кода при запуске приложения. Этот код может быть любым, от записи определенной информации до настройки базы данных, заданий cron и т. д. Мы не можем просто поместить этот код в конструктор, потому что требуемые переменные или службы могут быть еще не инициализированы. Это может привести к null pointer исключению или некоторым другим.
В процессе запуска после инициализации контекста Spring boot вызывает свой метод run() с аргументами командной строки, предоставленными приложению.
Чтобы сообщить Spring Boot о нашем интерфейсе commandlineRunner, мы можем либо реализовать его и добавить аннотацию @Component над классом, либо создать его bean-компонент с помощью @bean.
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);
}
}
}
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/<FILENAME.JAR HERE> --status=running
Это дает следующий вывод журнала:
In CommandLineRunnerImpl
status=running
Как мы видим, параметр не анализируется, а вместо этого интерпретируется как одно значение «status = running».
Чтобы получить доступ к аргументам командной строки в проанализированном формате, нам нужно использовать интерфейс ApplicationRunner. Мы рассмотрим это чуть позже.
Мы можем использовать аннотацию @Order, как показано ниже
@Component
@Order(1)
public class CommandLineRunnerImpl implements CommandLineRunner {
........
}
ApplicationArguments - это интерфейс, который доступен из srping boot 1.3 в пакете org.springframework.boot.
Он предоставляет различные способы доступа к аргументам, как показано ниже.
@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 имеют схожие функции, например:
Если нам не нужны аргументы командной строки, это лучший способ выполнить код после запуска приложения.
@Component
public class RunAfterStartup{
@EventListener(ApplicationReadyEvent.class)
public void runAfterStartup() {
System.out.println("Yaaah, I am running........");
}
Некоторые наиболее важные события spring boot,
Order используется совместно с другими ApplicationListeners того же типа, но не с ApplicationRunners или CommandLineRunners.
Метод @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 в приложение.
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.
Метод, предоставленный в 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.
Зачем нам нужно запускать код при запуске Spring Boot?
Нам нужно запускать метод при запуске приложения по многим причинам, например, для:- Записи важных событий или сообщения о запуске приложения
- Обработки базы данных или файлов, индексации, создания кэшей и т. д.
- Запуска фоновых процессов, таких как отправка уведомления, выборка данных из некоторой очереди и т. д.
Различные способы запуска метода после запуска Spring Boot
У каждого способа есть свои преимущества. Давайте рассмотрим подробнее, чтобы решить, что нам следует использовать:- интерфейс CommandLineRunner
- интерфейс ApplicationRunner
- события Spring Boot Application
- аннотацию @Postconstruct для метода
- интерфейс InitializingBean
- атрибут инициализации аннотации @bean
1. Использование интерфейса CommandLineRunner
CommandLineRunner - это функциональный интерфейс Spring Boot, который используется для запуска кода при запуске приложения. Он находится в пакете org.springframework.boot.В процессе запуска после инициализации контекста Spring boot вызывает свой метод run() с аргументами командной строки, предоставленными приложению.
Чтобы сообщить Spring Boot о нашем интерфейсе commandlineRunner, мы можем либо реализовать его и добавить аннотацию @Component над классом, либо создать его bean-компонент с помощью @bean.
Пример реализации интерфейса CommandLineRunner
@Componentpublic 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
@SpringBootApplicationpublic 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/<FILENAME.JAR HERE> --status=running
Это дает следующий вывод журнала:
In CommandLineRunnerImpl
status=running
Как мы видим, параметр не анализируется, а вместо этого интерпретируется как одно значение «status = running».
Чтобы получить доступ к аргументам командной строки в проанализированном формате, нам нужно использовать интерфейс ApplicationRunner. Мы рассмотрим это чуть позже.
Мы можем создать несколько CommandLineRunner в одном приложении. Используя интерфейс Ordered или аннотацию @Order, мы можем настроить порядок, в котором они должны запускаться. Меньшее значение означает более высокий приоритет. По умолчанию все компоненты создаются с самым низким приоритетом. Поэтому компоненты не указанные конфигурации order будут вызываться последними.Spring Boot добавляет интерфейс CommandLineRunner в процесс запуска. Следовательно, выброшенное исключение в commandlineRunner заставит процесс загрузки Spring прервать запуск.
Мы можем использовать аннотацию @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<String> getOptionNames() | Именам всех необязательных аргументов предшествует «-», например: –name = «stacktrace» |
List<String> getNonOptionArgs() | Возвращает необработанные необязательные аргументы. Аргументы без «-» |
boolean containsOption(String name) | Проверяет, присутствует ли имя в необязательных аргументах или нет |
List<String> 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: срабатывает, если есть исключение при запуске
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-компонента.
Связанные темы
- Spring boot project setup guide
- Springboot introduction tutorial
- Beginner tutorial for Spring data Java
- Method name to nested object query
- Spring boot custom banner generation
6 способов выполнения метода при старте Spring Boot приложения
При разработке на Spring Boot иногда нам нужно выполнить метод или фрагмент кода при запуске приложения. Этот код может быть любым, от записи определенной информации до настройки базы данных, заданий...
habr.com