1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Spring Bootの設定メモ

Last updated at Posted at 2021-05-18

これはなんですか?

私がSpring Bootのプロジェクトを建てるときにやってることをまとめた。
2021年5月18日時点の話です。
API受けサーバ向け、Thymeleafはもうやらないと決めたので割愛だけど多分なんか追加するだけでいけるはず・・。

主にMaven(pom.xml)。時々クラスやconfigure、関連クラスの配置。
ほんとにこれが正解なのか?と疑問があるところはあまり詳しく書いていない。
特にAWSまわりは情報が無く手探りだった。

ほぼ必ずやること

その時の最新版をチョイスする

現状だと 2.5.0 開発しているうちに最新版が出るかどうかでRCを使うか判断する。

Spring initializrでプロジェクトの雛形をダウンロードする

毎回細かく変わっていてやるので、基本的にメジャーバージョンが変わるのであれば以前のプロジェクトから引き継いだりしないほうがよい。
その際に groupartifact は関連レポジトリの有無などを含めきちんと設計、考慮しておく。

JavaのLTS向け設定(現状は11)

  <properties>
    <java.version>11</java.version>
  </properties>

lombok入れる

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      <scope>provided</scope>
    </dependency>

今どきボイラーコードをぐつぐつする人は居ないはず。

ModelMapper入れる

    <dependency>
      <groupId>io.github.yoshikawaa.modelmapper.spring.boot</groupId>
      <artifactId>modelmapper-spring-boot-starter</artifactId>
      <version>0.1.0</version>
      <type>pom</type>
    </dependency>

今どき単純なコンバーターを手で書く必要はあまりないけどたまにある。

OpenApi Generatorの組み込みとyamlの執筆

Swaggerなんとかはちょっとあくどい戦いを見てしまったので、Openapi Generatorを現状では支持しているが、開発状況によっては見捨てないといけないかもしれない。私は悲しい。

今どきREST APIのコントローラーだのクライアントだのをつるしで書く人はいないと信じる。

      <plugin>
        <groupId>org.openapitools</groupId>
        <artifactId>openapi-generator-maven-plugin</artifactId>
        <!-- RELEASE_VERSION -->
        <version>4.3.1</version>
        <!-- /RELEASE_VERSION -->
        <executions>
          <!-- コントローラー用 -->
          <execution>
            <id>api</id>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
              <language>spring</language>
              <configOptions>
                <generateApis>true</generateApis>
                <apiPackage>jp.nreso.project.api</apiPackage>
                <generateModels>true</generateModels>
                <modelPackage>jpjp.nreso.project.model</modelPackage>
                <generateSupportingFiles>false</generateSupportingFiles>
                <sourceFolder>src/main</sourceFolder>
              </configOptions>
              <output>${project.build.directory}/generated-sources/api</output>
            </configuration>
          </execution>
          <!-- クライアント用 通信先その1 -->
          <execution>
            <id>client1</id>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <inputSpec>${project.basedir}/src/main/resources/client1.yaml</inputSpec>
              <language>java</language>
              <configOptions>
                <generateApis>true</generateApis>
                <apiPackage>jp.nreso.project.client1</apiPackage>
                <generateModels>true</generateModels>
                <modelPackage>jp.nreso.project.client1.model</modelPackage>
                <sourceFolder>src/main</sourceFolder>
              </configOptions>
              <output>${project.build.directory}/generated-sources/client1/client</output>
            </configuration>
          </execution>
          <!-- クライアント用 通信先その2 -->
          <execution>
            <id>client2</id>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <inputSpec>${project.basedir}/src/main/resources/client2.yaml</inputSpec>
              <language>java</language>
              <configOptions>
                <generateApis>true</generateApis>
                <apiPackage>jp.nreso.project.client2</apiPackage>
                <generateModels>true</generateModels>
                <modelPackage>jp.nreso.project.client2.model</modelPackage>
                <sourceFolder>src/main</sourceFolder>
              </configOptions>
              <output>${project.build.directory}/generated-sources/client2/client</output>
            </configuration>
          </execution>
        </executions>
      </plugin>

上記だけだとダメで、生成されたコードからビルドのじゃまになるコードを消す、

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <executions>
          <execution>
            <phase>generate-sources</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <target>
                <delete dir="${project.basedir}/target/generated-sources/api/src/test"/>
                <delete dir="${project.basedir}/target/generated-sources/api/src/main/org"/>
                <delete
                  dir="${project.basedir}/target/generated-sources/api/src/main/resources"/>
                <delete dir="${project.basedir}/target/generated-sources/client1/client/src/test"/>
                <delete>
                  <fileset
                    dir="${project.basedir}/target/generated-sources/api/src/main/jp/nreso/api"
                    includes="*Controller.java"/>
                </delete>
                <delete dir="${project.basedir}/target/generated-sources/client2/src/test"/>
                <delete dir="${project.basedir}/target/generated-sources/client2/src/main/org"/>
                <delete>
                  <fileset
                    dir="${project.basedir}/target/generated-sources/client2/src/main/jp/nreso/api"
                    includes="*Controller.java"/>
                </delete>
              </target>
            </configuration>
          </execution>
        </executions>

生成したコードが使えるようにソースパスに追加。

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <executions>
          <execution>
            <phase>generate-sources</phase>
            <goals>
              <goal>add-source</goal>
            </goals>
            <configuration>
              <sources>
                <source>${basedir}/target/generated-sources/api/src/main</source>
                <source>${basedir}/target/generated-sources/client1/src/main</source>
                <source>${basedir}/target/generated-sources/client2/client/src/main</source>
              </sources>
            </configuration>
          </execution>
        </executions>
      </plugin>

手間かかるなー でもやっておかないと・・

Springdocの設定

開発時に書いたyamlを可視化、実行するために必要。

なお、本番環境ではこいつは殺さないといけないので、SwaggerController.javaにキルスイッチを実装しておく。
(探せばなんか公式のスイッチありそう)
また、複数のyamlはindex.htmlを書いて選べるようにしておくといいんじゃないかな。

pom.xml
      <plugin>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-maven-plugin</artifactId>
        <version>0.3</version>
        <executions>
          <execution>
            <phase>integration-test</phase>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
select.html
@Controller
public class SwaggerController {

    @Value("${springfox.documentation.swagger.enable:false}") // これがキルスイッチ
    private boolean enable;

    @RequestMapping("/")
    public String index() {
        return enable ? "select.html" : "index.html"; // disabledの場合はindex.htmlなどの魔除けでも404 not foundでもよい
    }

    @RequestMapping(value = "/api-docs/{api}",
        produces = { "application/json" },
        method = RequestMethod.GET)
    public ResponseEntity<Map> apiDocs(@ApiParam(value = "api", required = true) @PathVariable("api") String api) throws IOException {
        String yamlFile = api + ".yaml";
        if (enable) {
            Yaml yaml = new Yaml();
            InputStream is = new ClassPathResource(yamlFile).getInputStream();
            Map<String, Object> obj = yaml.load(is);
            return new ResponseEntity<>(obj, HttpStatus.OK);
        }
        throw new RuntimeException();
    }
}
select.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Hello</title>
  <meta charset="utf-8" />
</head>
<body>
<h1>Select your API</h1>
<p>
  <a href="swagger-ui/index.html?url=/api/api-docs/api">Controller</a>&nbsp;
  <a href="swagger-ui/index.html?url=/api/api-docs/client1">client 1</a>&nbsp;
  <a href="swagger-ui/index.html?url=/api/api-docs/client1">client 2</a>
</p>
</body>
</html>

Databaseアクセスのため、mybatisまたはdaoの設定

MySQLを使うことが多い

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.3</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>

Error Advisorの設定

プロジェクトの package を jp.nreso.project とした場合、どこでもいいのでエラーアドバイザを設定する。
具体的に言うとエラーハンドリングとよばれる類のもので、サービスから上がってきたもろもろのRuntimeExceptionを適切なHTTPレスポンスに変換すること。

この記事が参考になるかもしれません。
https://qiita.com/NagaokaKenichi/items/2f199134a881a776b717

以下は必要ならやること

Spring Security

HTTP Headerでの認証認可が必要ならConfuigureのクラス設定する。

JWT(Auth0)

JWTすこ。
今走ってるプロジェクトだとcognitoと連携しているもよう(別な人にPoC任せてるので、やってないので後で上がったものを見ておこう)

    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.10.3</version>
    </dependency>

キャッシュの設定

キャッシュ的なものが必要な場合、 Service の中にメンバ持ったりせず、キャッシュに委ねる。
実装がややめんどくさい。

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

ログイン系の処理の場合はLOG AppenderにMDCを設定

フィルタで下記を設定して、消す処理を入れる

Filter.java
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    if (認証必要ですよー) {
        MDC.put("deviceId", ユーザID);
    }
    try {
      chain.doFilter(request, response);
    } finally {
      MDC.clear();
    }
  }

これによってアクセスログにユーザIDを出せるからよき。

UT/mockの設定

たぶん initialzr がやってくれると思うけど一応。
あとmockのdependenciesを入れておく。

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

参照するレポジトリの設定

initializr に最初からついてるかもしれないけど、Springのものを書いておく。
Nexusなど立ててプライベートなmavenレポジトリを運用している人もここに追記する。

  <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
    </repository>
    <repository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
    </pluginRepository>
    <pluginRepository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>

AWSの場合

どんどん適当になってきてすまない。

Spring CloudやAWSのSDKの依存関係を設定

AWSのSDKをdependenciesに追加したり、Spring Cloudでなんとかなっていたり正解がよくわからないので恥ずかしくあまり書きたくない。

Log Appenderにアクセスログを追加

kubernetesのpodからCloud Watch Logsにログを送るのに必要。

現状これでなんとか・・なってそう。
githubで公開されてるやつのうち、1個以上使ってはいけないやつがある。少なくともこれはちゃんとずっとworkしてた。

pom.xml
    <dependency>
      <groupId>com.j256.cloudwatchlogbackappender</groupId>
      <artifactId>cloudwatchlogbackappender</artifactId>
      <version>2.1</version>
    </dependency>

こいつをこんなかんじのクラス作って。

AccessLogAppender.java
public class AccessLogAppender<E> extends ConsoleAppender<E> 

こんなかんじの設定おいてやらないと。

logback-access.xml
<configuration>
  <appender name="CONSOLE" class="jp.nreso.project.api.log.AccessLogAppender">
    <dateFormat>yyyyMMdd_HHmm</dateFormat>
    <encoder>
      <pattern>%h %l %user "%r" %s %b</pattern>
    </encoder>
  </appender>
  <appender-ref ref="CONSOLE"/>
</configuration>

ちゃんとに送られないもよう。
この道を通らなくても FluentDの設定をコンテナに対しを行えばよいはずだけど、どうしてもログの出力をJava側に置きたかった事情があったのです。

ECRのDockerの設定

CI/CD用にレポジトリのURLやクレデンシャルなどの情報をmaven実行時にパラメータで指定できるように_:(´ཀ`」 ∠):_

この先は文字がかすれて読めない

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?