yoshikipom Tech Blog

Spring BootでREST API, MQ consumer, CLI, 定期実行タスクを実行

Overview

Spring BootはWebアプリケーションを実行する以外にもいろいろできるのでいくつか紹介する。

REST API

application

Spring Bootで一番よくあるアプリケーション。 HTTPリクエストを送ったらJSONレスポンスを得られる。

Spring Boot Application + @RestControllerを持つクラスを実装すれば実装できる。

application class

@SpringBootApplication
public class ApiApplication {

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

}

controller example

@RestController
@RequestMapping("items")
public class ItemController {

  @GetMapping("")
  public List<Item> get() {
    return List.of(new Item("value"));
  }
}

https://github.com/yoshikipom/java/blob/main/spring-mvc/src/main/java/com/yoshikipom/dev/api/rest/ItemController.java

実行

$ curl --request GET \
  --url http://localhost:8080/items \
[{"key":"value"}]%

MQ consumer (worker)

application

Message Queueのconsumer. この例はkafkaの consumer (org.springframework.kafka:spring-kafka を利用)。 @KafkaListenerでトピックを指定。シリアライザーなど細かい設定はapplication.yamlに書ける。

application class

@SpringBootApplication
@EnableKafka
@Slf4j
public class ConsumerApplication {

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

  @KafkaListener(topics = "my_topic")
  public void receive(String value) {
    log.info("Received value: {}", value);
  }
}

application.yaml

spring:
  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      client-id: ${spring.application.name}
      group-id: ${spring.application.name}-group
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      auto-offset-reset: earliest

https://github.com/yoshikipom/java/blob/main/spring-mvc/src/main/java/com/yoshikipom/dev/consumer/ConsumerApplication.java

実行

kafkaに対してメッセージをproduce.

$ cat test.json
{"name":"myname"}
$ kcat -b localhost:9092 -t my_topic -P -J -l test.json

アプリケーションのログ

2022-08-01 00:25:38.553  INFO 54511 --- [ntainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : ${spring.application.name}-group: partitions assigned: [my_topic-0]
2022-08-01 00:30:04.773  INFO 54511 --- [ntainer#0-0-C-1] c.y.dev.consumer.ConsumerApplication     : Received value: {"name":"myname"}

コマンドラインアプリケーション

application

単発の処理を実行してすぐ終了するアプリケーション。 CommandLineRunnerの実装に@SpringBootApplicationをつける。 runメソッドの引数でコマンドライン引数を受け取れる。

@SpringBootApplication
@Slf4j
public class CliApplication implements CommandLineRunner {

  public static void main(String[] args) {
    SpringApplication.run(CliApplication.class, args);
    System.exit(0);
  }

  @Override
  public void run(String... args) throws Exception {
    log.info("arg size: {}", args.length);
    for (int i = 0; i < args.length; ++i) {
      log.info("args[{}]: {}", i, args[i]);
    }
  }
}

実行

jarに固めて実行してみる

$ java -jar spring-mvc-0.0.1-SNAPSHOT.jar arg1 arg2

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.0)

2022-08-01 00:33:47.676  INFO 54895 --- [           main] com.yoshikipom.dev.cli.CliApplication    : Starting CliApplication using Java 18 on yoshiki-mbp.local with PID 54895 (/Users/yoshiki/Documents/work/java/spring-mvc/build/libs/spring-mvc-0.0.1-SNAPSHOT.jar started by yoshiki in /Users/yoshiki/Documents/work/java/spring-mvc/build/libs)
2022-08-01 00:33:47.679  INFO 54895 --- [           main] com.yoshikipom.dev.cli.CliApplication    : No active profile set, falling back to 1 default profile: "default"
2022-08-01 00:33:48.732  INFO 54895 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-08-01 00:33:48.744  INFO 54895 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-08-01 00:33:48.744  INFO 54895 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.63]
2022-08-01 00:33:48.834  INFO 54895 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-08-01 00:33:48.834  INFO 54895 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1073 ms
2022-08-01 00:33:49.420  INFO 54895 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-08-01 00:33:49.436  INFO 54895 --- [           main] com.yoshikipom.dev.cli.CliApplication    : Started CliApplication in 2.275 seconds (JVM running for 2.708)
2022-08-01 00:33:49.441  INFO 54895 --- [           main] com.yoshikipom.dev.cli.CliApplication    : arg size: 2
2022-08-01 00:33:49.446  INFO 54895 --- [           main] com.yoshikipom.dev.cli.CliApplication    : args[0]: arg1
2022-08-01 00:33:49.446  INFO 54895 --- [           main] com.yoshikipom.dev.cli.CliApplication    : args[1]: arg2

定期実行アプリケーション

application

定期実行する処理。 interval(fixedRate)を設定する方法やcron形式で設定する方法がある。 @EnableSchedulingをapplicationクラスにつけて、@Scheduledをつけたメソッドを実装する。 上記のコマンドラインアプリケーションを定期的に実装しても同じことができるが、その場合アプリケーションの起動処理の後にやりたい処理が実行される。 こちらの場合は定期処理の前のアプリケーション実行はないが、常にSpring Bootアプリケーションは起動しておく必要がある。

@SpringBootApplication
@EnableScheduling
@Slf4j
public class ScheduledApplication {

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

  //  @Scheduled(cron = "0 * 9 * * ?")
  @Scheduled(fixedRate = 1000)
  public void receive() {
    log.info("run scheduled task: {}", LocalDateTime.now());
  }
}

実行

アプリのログ

...
2022-08-01 00:36:32.601  INFO 54945 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-08-01 00:36:32.612  INFO 54945 --- [   scheduling-1] c.y.dev.scheduled.ScheduledApplication   : run scheduled task: 2022-08-01T00:36:32.612477
2022-08-01 00:36:32.615  INFO 54945 --- [           main] c.y.dev.scheduled.ScheduledApplication   : Started ScheduledApplication in 4.381 seconds (JVM running for 4.891)
2022-08-01 00:36:33.613  INFO 54945 --- [   scheduling-1] c.y.dev.scheduled.ScheduledApplication   : run scheduled task: 2022-08-01T00:36:33.613535
2022-08-01 00:36:34.616  INFO 54945 --- [   scheduling-1] c.y.dev.scheduled.ScheduledApplication   : run scheduled task: 2022-08-01T00:36:34.616480

まとめ

Spring Bootのさまざまな機能をWeb API実装以外にも使える。

追記

Web APIはGraghQLやgRPCにも対応できる。 Batch処理に関しては大規模な処理ではSpring Batch (https://spring.io/projects/spring-batch) なども使える。 consumerはspring cloud streamを使った方がいろいろなMQに対応できる。