📝

【Spring Boot】素のSpring Bootでできること

2024/06/29に公開

なにも追加しない Spring Boot プロジェクトでも使える基本的な機能をまとめました。

1. 動作環境

  • macOS 14.5
  • Java 21
  • Gradle 8.8
  • Spring Boot 3.3.1

2. DI コンテナの利用

Spring Boot は Bean と呼ばれるオブジェクトを管理し、それらの Bean 間の依存関係を注入して任意の場所で参照できます。

2.1. Bean の定義方法

Bean の定義には以下の方法があります[1]。Bean は一意の名前で管理されます。

  • アノテーションベースの設定
  • Java ベースの設定
  • XML ベースの設定

2.1.1. アノテーションベースの設定

@Component@Service@Repository@Controller などのアノテーションをクラスに付与すると、そのインスタンスが Bean として管理されます。
Bean 名はクラス名の先頭を小文字にしたものになります。

DemoConfBean.java
package com.example.demo;

import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    public void helloworld() {
        System.out.println("Hello, world!");
    }

}

2.1.2. Java ベースの設定

@Configuration アノテーションを付与したクラス内で @Bean アノテーションを付与したメソッドを定義すると、そのメソッドの戻り値が Bean として管理されます。
Bean 名はメソッド名になります。

DemoConfig.java
package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DemoConfig {

    @Bean
    DemoConfBean demoConfBean() {
        return new DemoConfBean();
    }

}

2.1.3. XML ベースの設定

XML ファイル内で Bean とその依存関係を定義し、@ImportResource アノテーションでインポートします。
Bean 名は<bean>タグの id になります。

beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="demoXml" class="com.example.demo.DemoXml"/>
</beans>
DemoApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource("classpath:beans.xml")
public class DemoApplication {

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

}

2.2. Bean の参照方法

Bean の参照には以下の方法があります。

  • コンストラクタインジェクション
  • セッターインジェクション
  • フィールドインジェクション
  • @Bean アノテーションを使ったインジェクション
  • ApplicationContext を使用した Bean の取得

2.2.1. コンストラクタインジェクション

@Autowired アノテーションを付与したコンストラクタを使用してインジェクションします。

DemoComponent.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    private final DemoService demoService;

    @Autowired
    public DemoComponent(DemoService demoService) {
        this.demoService = demoService;
    }

    public void exec() {
        demoService.service();
    }

}

2.2.2. セッターインジェクション

@Autowired アノテーションを付与したセッターメソッドを使用してインジェクションします。

DemoComponent.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    private DemoService demoService;

    @Autowired
    void setDemoService(DemoService demoService) {
        this.demoService = demoService;
    }

    public void exec() {
        demoService.service();
    }

}

2.2.3. フィールドインジェクション

フィールドに @Autowired アノテーションを付与してインジェクションします。

DemoComponent.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    @Autowired
    private DemoService demoService;

    public void exec() {
        demoService.service();
    }

}

2.2.4. @Bean アノテーションを使ったインジェクション

@Bean アノテーションを付与したメソッドの引数にインジェクションします。

DemoConfig.java
package com.example.demo;

import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DemoConfig {

    @Bean
    ApplicationRunner exec(DemoService demoService) {
        return args -> {
            demoService.service();
        };
    }

}

2.2.5. ApplicationContext を使用した Bean の取得

ApplicationContextgetBean メソッドで取得します。

DemoConfig.java
package com.example.demo;

import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DemoConfig {

    @Bean
    ApplicationRunner exec(ApplicationContext applicationContext) {
        return args -> {
            DemoService demoService = applicationContext.getBean(DemoService.class);
            demoService.service();
        };
    }

}

3. 自動実行 Bean の利用

CommandLineRunner または ApplicationRunner インターフェースを実装した Bean はアプリケーション起動時に自動実行されます。例えば以下のクラスを追加して実行すると Hello, world! が表示されます。CommandLineRunnerApplicationRunnerrun メソッドの引数に違いがあります。

DemoRunner.java
package com.example.demo;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class DemoRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Hello, world!");
    }

}

4. コマンドライン引数の参照

コマンドライン引数を参照するには ApplicationArguments を参照します。ApplicationArguments は Spring Boot によって自動的に Bean として登録されています。

DemoComponent.java
package com.example.demo;

import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    private final ApplicationArguments applicationArguments;

    @Autowired
    public DemoComponent(ApplicationArguments applicationArguments) {
        this.applicationArguments = applicationArguments;
    }

    public void exec() {
        System.out.println(applicationArguments.getOptionNames());
    }

}

5. 終了コードの設定

終了コードを返す必要がある場合には、まず ExitCodeGenerator を実装した Bean を定義し getExitCode メソッドをオーバーライドします。

DemoExitCodeGenerator.java
package com.example.demo;

import org.springframework.boot.ExitCodeGenerator;
import org.springframework.stereotype.Component;

@Component
public class DemoExitCodeGenerator implements ExitCodeGenerator {

    private int exitCode = 0;

    public void setExitCode(int exitCode) {
        this.exitCode = exitCode;
    }

    @Override
    public int getExitCode() {
        return exitCode;
    }

}

次に以下のように main メソッドの SpringApplication.run の戻り値を SpringApplication.exit に渡し System.exit を呼び出します。

DemoApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

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

}

そして終了コードを設定する箇所では ExitCodeGenerator の実装の Bean を参照して終了コードを設定します。

DemoComponent.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    private final DemoExitCodeGenerator demoExitCodeGenerator;

    @Autowired
    public DemoComponent(DemoExitCodeGenerator demoExitCodeGenerator) {
        this.demoExitCodeGenerator = demoExitCodeGenerator;
    }

    public void exec() {
        /*
         * 何らかの処理
         */
        demoExitCodeGenerator.setExitCode(1); // 終了コードの設定
    }

}

6. プロパティの参照

Spring Boot アプリケーションのパラメータを定義する application.propertiesapplication.yml には独自のカスタム設定値も自由に定義できます。
これらを参照するには以下の様な方法があります。

  • @Value アノテーションを使用する
  • @ConfigurationProperties アノテーションを使用する
  • Environment オブジェクトを使用する

6.1. @Value アノテーションを使用する

フィールドにプロパティ名を指定した @Value アノテーションを付与します。

DemoComponent.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    @Value("${demo.name}")
    private String name;

    @Value("${demo.version}")
    private String version;

    @Value("${demo.description}")
    private String description;

    public void exec() {
        System.out.println(name);
        System.out.println(version);
        System.out.println(description);
    }

}

6.2. @ConfigurationProperties アノテーションを使用する

@Configuration アノテーション及び、プロパティ名のプレフィックスを指定した@ConfigurationProperties アノテーションを付与したクラスを作成します。

DemoProperties.java
package com.example.demo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "demo")
public class DemoProperties {

    private String name;
    private String version;
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

}

6.3. Environment オブジェクトを使用する

Environment オブジェクトの getProperty メソッドにプロパティ名を指定します。

DemoComponent.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    private final Environment environment;

    @Autowired
    public DemoComponent(Environment environment) {
        this.environment = environment;
    }

    public void exec() {
        System.out.println(environment.getProperty("demo.name"));
        System.out.println(environment.getProperty("demo.version"));
        System.out.println(environment.getProperty("demo.description"));
    }

}

7. ログの出力

ログライブラリが含まれているので以下の様にLoggerを宣言してそのままログの出力を行えます。

DemoComponent.java
package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class DemoComponent {

    private final Logger logger = LoggerFactory.getLogger(DemoComponent.class);

    public void exec() {
        logger.info("info!");
    }

}

デフォルトではコンソールのみにログが出力されます。プロパティにはログレベルを指定するlogging.level.*やログファイル名を指定するlogging.file.nameなどがあり簡単に設定できます。より詳細な設定をしたい場合は logback-spring.xml [2]または logback.xml などを作成し、src/main/resources ディレクトリに配備するかlogging.configプロパティに指定します。
Spring Boot では以下のライブラリをサポートしています。

  • SLF4J
  • Logback
  • Log4j2
  • JUL
  • Commons Logging

8. ユニットテスト

JUnit 5、AssertJ、Mockito などのライブラリが含まれています。@SpringBootTest アノテーションをテストクラスに付与することで Bean をテストクラスにインジェクションしてテストすることができます。

DemoCalculatorTests.java
package com.example.demo;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoCalculatorTests {

    @Autowired
    private DemoCalculator demoCalculator;

    @Test
    void testAdd() {
        // 加算のテスト
        assertThat(demoCalculator.add(10, 20)).isEqualTo(30);
    }

}

@MockBean アノテーションを使用することで テスト対象コンポーネントが依存している Bean を簡単にモックに置き換えることができます。

DemoComponentTests.java
package com.example.demo;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
class DemoComponentTests {

    @Autowired
    private DemoComponent demoComponent;

    @MockBean
    private DemoDice demoDice; // DemoComponentが依存しているBeanのモック

    @Test
    void testExec1() {
        // モック(demoDice)のroll()呼び出しの戻り値を2にする
        when(demoDice.roll()).thenReturn(2);

        assertThat(demoComponent.exec()).isEqualTo("偶数");
    }

    @Test
    void testExec2() {
        // モック(demoDice)のroll()呼び出しの戻り値を1にする
        when(demoDice.roll()).thenReturn(1);

        assertThat(demoComponent.exec()).isEqualTo("奇数");
    }

}
脚注
  1. Spring Boot によって自動的に定義される Bean もあります。 ↩︎

  2. Spring Boot 固有の拡張機能をサポートしている。 ↩︎

Discussion