かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その6 )( Checkstyle を 8.19 → 8.28 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その5 )( Release Notes を見て必要な箇所を変更する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Checkstyle を 8.19 → 8.28 へバージョンアップします。
    • 最新版の google_checks.xml の内容も反映します。

参照したサイト・書籍

  1. checkstyle
    https://checkstyle.sourceforge.io/

  2. checkstyle/checkstyle - checkstyle/src/main/resources/google_checks.xml
    https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml

目次

  1. build.gradle を変更する
  2. エラーの原因を取り除く
    1. LineLength を記述する位置を移動する
    2. JavadocMethod の設定を最新の google_checks.xml からコピーする
  3. IntelliJ IDEA の CheckStyle-IDEA Plugin を最新版に update した後 8.27 に変更する
  4. 最新版の google_checks.xml から設定をコピーする

手順

build.gradle を変更する

checkstyle {
    configFile = file("${rootProject.projectDir}/config/checkstyle/google_checks.xml")
    toolVersion = "8.28"
    sourceSets = [project.sourceSets.main]
}
  • toolVersion = "8.19"toolVersion = "8.28" に変更します。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新した後、clean タスク実行 → build タスクを実行すると checkstyleMain タスクで Unable to create Root Module: config {D:\project-springboot\ksbysample-webapp-lending\config\checkstyle\google_checks.xml} ... のメッセージが出力されて中断しました。

f:id:ksby:20200101111335p:plain

エラーの原因を取り除く

LineLength を記述する位置を移動する

8.20、8.21、... と1つずつバージョンアップしてみたところ、8.24 で Unable to create Root Module ... のメッセージが出ました。

Release 8.24 を確認したところ、Change LineLength Check parent from TreeWalker to Checker. の記述がありました。

LineLength を記述する位置を <module name="TreeWalker">...</module> の中から、

<module name = "Checker">
    ..........

    <module name="TreeWalker">
        ..........
        <module name="LineLength">
            <property name="max" value="120"/>
            <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
        </module>

<module name="Checker">...</module> の直下に移動します。

<module name = "Checker">
    ..........

    <module name="LineLength">
        <property name="max" value="120"/>
        <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
    </module>

    <module name="TreeWalker">
        ..........

これで 8.24 ではメッセージは出なくなりました。

JavadocMethod の設定を最新の google_checks.xml からコピーする

更に 8.25、8.26、... と1つずつバージョンアップしてみたところ、今度は 8.28 で Unable to create Root Module ... のメッセージが出ました。

Release 8.28 を確認したところ JavadocMethod に変更が入っているようなのですが、今ひとつ分かりません。config/checkstyle/google_checks.xml から <module name="JavadocMethod">...</module>コメントアウトするとメッセージは出なくなりましたので、JavadocMethod の定義でエラーが出ていることは確かです。

1行ずつコメントアウトしてみたところ <property name="allowMissingThrowsTags" value="true"/><property name="allowThrowsTagsForSubclasses" value="true"/>コメントアウトするとエラーが出なくなりました。Release 8.28Remove properties related to class loading from JavadocMethod . の影響のようです。

        <module name="JavadocMethod">
            <property name="scope" value="public"/>
            <property name="allowMissingParamTags" value="true"/>
<!--            <property name="allowMissingThrowsTags" value="true"/>-->
            <property name="allowMissingReturnTag" value="true"/>
            <property name="allowedAnnotations" value="Override, Test"/>
<!--            <property name="allowThrowsTagsForSubclasses" value="true"/>-->
        </module>

最新版の google_checks.xml を見たところ設定が結構変わっていたので、そのままコピーすることにします。

        <module name="JavadocMethod">
            <property name="scope" value="public"/>
            <property name="allowMissingParamTags" value="true"/>
            <property name="allowMissingReturnTag" value="true"/>
            <property name="allowedAnnotations" value="Override, Test"/>
            <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
        </module>

これで 8.28 ではメッセージは出なくなりました。

clean タスク実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20200101124031p:plain

IntelliJ IDEA の CheckStyle-IDEA Plugin を最新版に update した後 8.27 に変更する

CheckStyle-IDEA Plugin を最新版に update した後、指定できるバージョンが 8.27 までだったので 8.27 を指定します。

f:id:ksby:20200101124559p:plain

最新版の google_checks.xml から設定をコピーする

最新版の google_checks.xml を見ていたら結構設定が異なっていたので、出来るだけ反映することにします。

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
        "https://checkstyle.org/dtds/configuration_1_3.dtd">

<!--
    Checkstyle configuration that checks the Google coding conventions from Google Java Style
    that can be found at https://google.github.io/styleguide/javaguide.html
    Checkstyle is very configurable. Be sure to read the documentation at
    http://checkstyle.org (or in your downloaded distribution).
    To completely disable a check, just comment it out or delete it from the file.
    To suppress certain violations please review suppression filters.
    Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
 -->

<module name = "Checker">
    <property name="charset" value="UTF-8"/>

    <property name="severity" value="warning"/>

    <property name="fileExtensions" value="java, properties, xml"/>
    <!-- Excludes all 'module-info.java' files              -->
    <!-- See https://checkstyle.org/config_filefilters.html -->
    <module name="BeforeExecutionExclusionFileFilter">
        <property name="fileNamePattern" value="module\-info\.java$"/>
    </module>
    <!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
    <module name="SuppressionFilter">
        <property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
                  default="checkstyle-suppressions.xml" />
        <property name="optional" value="true"/>
    </module>

    <!-- Checks for whitespace                               -->
    <!-- See http://checkstyle.org/config_whitespace.html -->
    <module name="FileTabCharacter">
        <property name="eachLine" value="true"/>
    </module>

    <module name="LineLength">
        <property name="fileExtensions" value="java"/>
        <property name="max" value="120"/>
        <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
    </module>

    <module name="TreeWalker">
        <module name="OuterTypeFilename"/>
        <module name="IllegalTokenText">
            <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
            <property name="format"
                      value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
            <property name="message"
                      value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
        </module>
        <module name="AvoidEscapedUnicodeCharacters">
            <property name="allowEscapesForControlCharacters" value="true"/>
            <property name="allowByTailComment" value="true"/>
            <property name="allowNonPrintableEscapes" value="true"/>
        </module>
        <!-- import 文の記述は IntelliJ IDEA 任せにしているのでチェックしない -->
        <!--<module name="AvoidStarImport"/>-->
        <module name="OneTopLevelClass"/>
        <module name="NoLineWrap">
            <property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
        </module>
        <module name="EmptyBlock">
            <property name="option" value="TEXT"/>
            <property name="tokens"
                      value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
        </module>
        <module name="NeedBraces">
            <property name="tokens"
                      value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
        </module>
        <module name="LeftCurly">
            <property name="tokens"
                      value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
                    INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
                    LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
                    LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
                    OBJBLOCK, STATIC_INIT"/>
        </module>
        <module name="RightCurly">
            <property name="id" value="RightCurlySame"/>
            <property name="tokens"
                      value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
                    LITERAL_DO"/>
        </module>
        <module name="RightCurly">
            <property name="id" value="RightCurlyAlone"/>
            <property name="option" value="alone"/>
            <property name="tokens"
                      value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
                    INSTANCE_INIT, ANNOTATION_DEF"/>
        </module>
        <module name="WhitespaceAround">
            <property name="allowEmptyConstructors" value="true"/>
            <property name="allowEmptyLambdas" value="true"/>
            <property name="allowEmptyMethods" value="true"/>
            <property name="allowEmptyTypes" value="true"/>
            <property name="allowEmptyLoops" value="true"/>
            <property name="tokens"
                      value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
                    BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
                    LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
                    LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
                     LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
                     NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
                     SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
            <message key="ws.notFollowed"
                     value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
            <message key="ws.notPreceded"
                     value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
        </module>
        <module name="OneStatementPerLine"/>
        <module name="MultipleVariableDeclarations"/>
        <module name="ArrayTypeStyle"/>
        <module name="MissingSwitchDefault"/>
        <module name="FallThrough"/>
        <module name="UpperEll"/>
        <module name="ModifierOrder"/>
        <module name="EmptyLineSeparator">
            <property name="tokens"
                      value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
                    STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
            <property name="allowNoEmptyLineBetweenFields" value="true"/>
        </module>
        <module name="SeparatorWrap">
            <property name="id" value="SeparatorWrapDot"/>
            <property name="tokens" value="DOT"/>
            <property name="option" value="nl"/>
        </module>
        <module name="SeparatorWrap">
            <property name="id" value="SeparatorWrapComma"/>
            <property name="tokens" value="COMMA"/>
            <property name="option" value="nl"/>
        </module>
        <module name="SeparatorWrap">
            <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
            <property name="id" value="SeparatorWrapEllipsis"/>
            <property name="tokens" value="ELLIPSIS"/>
            <property name="option" value="EOL"/>
        </module>
        <module name="SeparatorWrap">
            <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
            <property name="id" value="SeparatorWrapArrayDeclarator"/>
            <property name="tokens" value="ARRAY_DECLARATOR"/>
            <property name="option" value="EOL"/>
        </module>
        <module name="SeparatorWrap">
            <property name="id" value="SeparatorWrapMethodRef"/>
            <property name="tokens" value="METHOD_REF"/>
            <property name="option" value="nl"/>
        </module>
        <module name="PackageName">
            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
            <message key="name.invalidPattern"
                     value="Package name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="TypeName">
            <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF"/>
            <message key="name.invalidPattern"
                     value="Type name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="MemberName">
            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
            <message key="name.invalidPattern"
                     value="Member name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="ParameterName">
            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
            <message key="name.invalidPattern"
                     value="Parameter name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="LambdaParameterName">
            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
            <message key="name.invalidPattern"
                     value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="CatchParameterName">
            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
            <message key="name.invalidPattern"
                     value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="LocalVariableName">
            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
            <message key="name.invalidPattern"
                     value="Local variable name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="ClassTypeParameterName">
            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
            <message key="name.invalidPattern"
                     value="Class type name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="MethodTypeParameterName">
            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
            <message key="name.invalidPattern"
                     value="Method type name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="InterfaceTypeParameterName">
            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
            <message key="name.invalidPattern"
                     value="Interface type name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="NoFinalizer"/>
        <module name="GenericWhitespace">
            <message key="ws.followed"
                     value="GenericWhitespace ''{0}'' is followed by whitespace."/>
            <message key="ws.preceded"
                     value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
            <message key="ws.illegalFollow"
                     value="GenericWhitespace ''{0}'' should followed by whitespace."/>
            <message key="ws.notPreceded"
                     value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
        </module>
        <module name="Indentation">
            <property name="basicOffset" value="4"/>
            <property name="braceAdjustment" value="0"/>
            <property name="caseIndent" value="4"/>
            <property name="throwsIndent" value="4"/>
            <property name="lineWrappingIndentation" value="4"/>
            <property name="arrayInitIndent" value="4"/>
        </module>
        <module name="AbbreviationAsWordInName">
            <property name="ignoreFinal" value="false"/>
            <property name="allowedAbbreviationLength" value="1"/>
            <property name="tokens"
                      value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
                    PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF"/>
        </module>
        <!-- Doma2 の Dao interface で警告が出て対応のしようがないのでコメントアウトする -->
        <!--<module name="OverloadMethodsDeclarationOrder"/>-->
        <module name="VariableDeclarationUsageDistance">
            <!-- allowedDistance のデフォルト値は 3 だが、少し短すぎるので 10 に変更する -->
            <property name="allowedDistance" value="10"/>
        </module>
        <!-- import 文の順序は IntelliJ IDEA 任せにしているのでチェックしない -->
        <!--<module name="CustomImportOrder">-->
        <!--<property name="sortImportsInGroupAlphabetically" value="true"/>-->
        <!--<property name="separateLineBetweenGroups" value="true"/>-->
        <!--<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>-->
        <!--</module>-->
        <module name="MethodParamPad">
            <property name="tokens"
                      value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
                    SUPER_CTOR_CALL, ENUM_CONSTANT_DEF"/>
        </module>
        <module name="NoWhitespaceBefore">
            <property name="tokens"
                      value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
            <property name="allowLineBreaks" value="true"/>
        </module>
        <module name="ParenPad">
            <property name="tokens"
                      value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
                    EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
                    LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
                    METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA"/>
        </module>
        <module name="OperatorWrap">
            <property name="option" value="NL"/>
            <property name="tokens"
                      value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
                    LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
        </module>
        <module name="AnnotationLocation">
            <property name="id" value="AnnotationLocationMostCases"/>
            <property name="tokens"
                      value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
        </module>
        <module name="AnnotationLocation">
            <property name="id" value="AnnotationLocationVariables"/>
            <property name="tokens" value="VARIABLE_DEF"/>
            <property name="allowSamelineMultipleAnnotations" value="true"/>
        </module>
        <module name="NonEmptyAtclauseDescription">
            <!-- @throws はチェックの対象から外す -->
            <property name="javadocTokens" value="PARAM_LITERAL,RETURN_LITERAL,DEPRECATED_LITERAL"/>
        </module>
        <module name="InvalidJavadocPosition"/>
        <module name="JavadocTagContinuationIndentation"/>
        <!-- Javadoc の1行目のフォーマットを指定するルールだが、今はそこまで真似られないのでコメントアウトする -->
        <!--<module name="SummaryJavadoc">-->
        <!--<property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>-->
        <!--</module>-->
        <module name="JavadocParagraph"/>
        <module name="AtclauseOrder">
            <property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
            <property name="target"
                      value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
        </module>
        <module name="JavadocMethod">
            <property name="scope" value="public"/>
            <property name="allowMissingParamTags" value="true"/>
            <property name="allowMissingReturnTag" value="true"/>
            <property name="allowedAnnotations" value="Override, Test"/>
            <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
        </module>
        <module name="MissingJavadocMethod">
            <property name="scope" value="public"/>
            <property name="minLineCount" value="2"/>
            <property name="allowedAnnotations" value="Override, Test"/>
            <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
        </module>
        <module name="MethodName">
            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
            <message key="name.invalidPattern"
                     value="Method name ''{0}'' must match pattern ''{1}''."/>
        </module>
        <module name="SingleLineJavadoc">
            <property name="ignoreInlineTags" value="false"/>
        </module>
        <module name="EmptyCatchBlock">
            <property name="exceptionVariableName" value="expected"/>
        </module>
        <module name="CommentsIndentation">
            <property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
        </module>
        <!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
        <module name="SuppressionXpathFilter">
            <property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
                      default="checkstyle-xpath-suppressions.xml" />
            <property name="optional" value="true"/>
        </module>
    </module>
</module>
  • <!DOCTYPE module PUBLIC ...> の記述が異なっていたのでコピーします。
  • ファイルの一番上に記述されているコメントが異なっていたのでコピーします。
  • <module name="BeforeExecutionExclusionFileFilter">...</module> をコピーします。
  • <module name="SuppressionFilter">...</module> をコピーします。
  • <module name="LineLength">...</module><property name="fileExtensions" value="java"/> を追加します。
  • <module name="NoLineWrap"/><module name="NoLineWrap">...</module> に変更して <property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/> を追加します。
  • <module name="NeedBraces"/><module name="NeedBraces">...</module> に変更して <property name="tokens" value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/> を追加します。
  • <module name="LeftCurly"/><module name="LeftCurly">...</module> に変更して <property name="tokens" value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF, INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF, OBJBLOCK, STATIC_INIT"/> を追加します。
  • <module name="RightCurly">...</module><property name="tokens" ...>value 属性に ANNOTATION_DEF を追加します。
  • <module name="WhitespaceAround">...</module> に以下の2行を追加します。
    • <property name="allowEmptyLambdas" value="true"/>
    • <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND, LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
  • <module name="EmptyLineSeparator">...</module> に以下の1行を追加します。
    • <property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF, STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
  • <module name="SeparatorWrap"><property name="id" value="SeparatorWrapEllipsis"/>...</module> を追加します。
  • <module name="SeparatorWrap"><property name="id" value="SeparatorWrapArrayDeclarator"/>...</module> を追加します。
  • <module name="SeparatorWrap"><property name="id" value="SeparatorWrapMethodRef"/>...</module> を追加します。
  • <module name="TypeName">...</module><property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF"/> を追加します。
  • <module name="LambdaParameterName">...</module> を追加します。
  • <module name="LocalVariableName">...</module> から <property name="tokens" value="VARIABLE_DEF"/> を削除します。
  • <module name="AbbreviationAsWordInName">...</module> に以下の1行を追加します。
    • <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF, PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF"/>
  • <module name="MethodParamPad"/><module name="MethodParamPad">...</module> に変更して <property name="tokens" value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF, SUPER_CTOR_CALL, ENUM_CONSTANT_DEF"/> を追加します。
  • <module name="NoWhitespaceBefore">...</module> を追加します。
  • <module name="ParenPad"/><module name="ParenPad">...</module> に変更して <property name="tokens" value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF, EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL, METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA"/> を追加します。
  • <module name="InvalidJavadocPosition"/> を追加します。
  • <module name="MissingJavadocMethod">...</module> を追加します。
  • <module name="CommentsIndentation"/><module name="CommentsIndentation">...</module> に変更して <property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/> を追加します。
  • <module name="SuppressionXpathFilter">...</module> を追加します。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20200101164354p:plain

ただし CheckStyle-IDEA Plugin がまだ 8.27 までしか対応していないので、頻繁に下の画像のダイアログが IntelliJ IDEA の右下に表示されます(build.gradle に記述する checkstyle のバージョンを 8.27 にすると build タスク実行時に同じエラーメッセージが出力されます)。

f:id:ksby:20200101164834p:plain

ということは最新版の google_checks.xmlcheckstyle の最新バージョンに対応しているということですね。今度から checkstyle のバージョンアップでエラーが出たら最初に差分を確認することにしましょう。

履歴

2020/01/01
初版発行。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その5 )( Release Notes を見て必要な箇所を変更する )

概要

記事一覧はこちらです。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その4 )( Spring Boot を 2.1.11 → 2.2.2 へバージョンアップする ) の続きです。

参照したサイト・書籍

  1. Javaパフォーマンスチューニング基礎
    https://www.slideshare.net/setoazusa/tuning-28987271

    • -XX:TieredStopAtLevel=1 オプションについて記載されています。
  2. Inspection when calling a @Bean method directly in a @Configuration where proxyBeanMethods is disabled
    https://youtrack.jetbrains.com/issue/IDEA-208951?_ga=2.42061037.931272388.1574489375-1608831281.1569753226

目次

  1. モジュールが com.sun.mail:jakarta.mail に変わっても package はまだ javax.mail でした
  2. Freemarker のテンプレートの拡張子を *.ftl*.ftlh に変更する
  3. 開発環境で Web アプリを起動する時には -XX:TieredStopAtLevel=1 オプションを指定する
  4. logging.filelogging.file.name に変更する
  5. メモ書き

手順

モジュールが com.sun.mail:jakarta.mail に変わっても package はまだ javax.mail でした

Spring Boot 2.2 Release Notes - Jakarta EE dependenciescom.sun.mail:javax.mailcom.sun.mail:jakarta.mail に変更したとの記述がありました。

gradlew dependencies を実行してみると確かに spring-boot-starter-mail が依存しているモジュールが com.sun.mail:jakarta.mail に変わっています。

+--- org.springframework.boot:spring-boot-starter-mail -> 2.2.2.RELEASE
|    +--- org.springframework.boot:spring-boot-starter:2.2.2.RELEASE (*)
|    +--- org.springframework:spring-context-support:5.2.2.RELEASE (*)
|    \--- com.sun.mail:jakarta.mail:1.6.4
|         \--- com.sun.activation:jakarta.activation:1.2.1

ということは package も javax.mail.~jakarta.mail.~ に変更されるのかな?と思いましたが、こちらはまだ javax.mail.~ のままでした。

f:id:ksby:20191231203109p:plain

Freemarker のテンプレートの拡張子を *.ftl*.ftlh に変更する

Spring Boot 2.2 Release Notes - Freemarker templates configurationFreemarker のテンプレートの拡張子が *.ftl*.ftlh に変更されたとの記述があったので、以下のファイルの拡張子を変更します。

  • src/main/resources/templates/mail/mail001-body.ftlh
  • src/main/resources/templates/mail/mail002-body.ftlh
  • src/main/resources/templates/mail/mail003-body.ftlh

Web アプリを起動して拡張子を変更したテンプレートでメールが送信されることを確認します。

f:id:ksby:20191231211049p:plain

開発環境で Web アプリを起動する時には -XX:TieredStopAtLevel=1 オプションを指定する

Spring Boot 2.2 Release Notes - Performance improvements に以下の記述がありました。

When launching an application at development time with bootRun in Gradle or spring-boot:run in Maven, the JVM will be configured with flags (-Xverify:none and -XX:TieredStopAtLevel=1) to optimise it for reduced launch time. When running on JDK 13 -Xverify:none is not specified as it has been deprecated.

Javaパフォーマンスチューニング基礎 によると 64bit 版の VM でも最初は Client VM になるオプションとのことでした。

実際に IntelliJ IDEA のメインメニューから「Run」-「Edit Configurations...」を選択して「Run/Debug Configurations」ダイアログを開き、Spring Boot のアプリケーションの「VM options」の設定に -XX:TieredStopAtLevel=1 オプションを指定した時と指定していない時の Root WebApplicationContext: initialization completed in ... ms のメッセージに表示される時間を比較してみます。

指定していない時は 5174、4995、5422、5253、5277 でした。

指定した時は 3414、5198、5496、5110、3590 でした。

必ず速くなる訳ではないようですが、速い時には 1.5 秒程度起動時間が短くできるようです。注意が必要なオプションという訳でもないようなので、指定するようにします。build.gradle の bootRun の設定にも追加します。

bootRun {
    jvmArgs = jvmArgsForTask +
            [
                    "-Dspring.profiles.active=develop",
                    "-XX:TieredStopAtLevel=1"
            ]
}

logging.filelogging.file.name に変更する

Spring Boot 2.2 Release Notes - Deprecations in Spring Boot 2.2 に記述があったので、src/main/resources/application-product.properties 内の設定を logging.filelogging.file.name に変更します。また使用しなくなった slowquery.logging.file の設定が残っていたので削除します。

server.tomcat.basedir=D:/webapps/ksbysample-webapp-lending
logging.file.name=${server.tomcat.basedir}/logs/ksbysample-webapp-lending.log

メモ書き

  • Spring Boot 2.2 Release Notes - Performance improvements@Configuration アノテーションproxyBeanMethods=false が指定できるようになったとの記述があり、追加すると起動時間とメモリ使用量を削減できるとのことなのですが、 Inspection when calling a @Bean method directly in a @Configuration where proxyBeanMethods is disabled には If you disable proxyBeanMethods , the proxy is no longer created and calling the method simply invokes it again (here returning a new instance every time so you have no guarantee you're actually injecting the corresponding bean in the context). という記述がありました。

    GitHub の Spring Boot のレポジトリで proxyBeanMethods=false で検索すると、シンプルに new して return する Bean の場合には何でも指定されているように見えます。Spring Boot 2.2 からどうもそういう方針のようです。

    今回 Web アプリの方には追加しませんが、覚えておきましょう。

  • Logback max historylogback-spring.xml で設定している <maxHistory> の設定を logging.file.max-history で設定できるようになったという記述がありますが、変更する必要性を感じなかったので logback-spring.xml に残しました。

履歴

2019/12/31
初版発行。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その4 )( Spring Boot を 2.1.11 → 2.2.2 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その3 )( Gradle を 5.6.4 → 6.0.1 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Spring Boot を 2.1.11 → 2.2.2 へバージョンアップします。

参照したサイト・書籍

  1. Issue 438464: json displayed with wrong encoding (i.e. not unicode)
    https://bugs.chromium.org/p/chromium/issues/detail?id=438464

  2. Bug 197369 - JSON displayed with wrong encoding when loaded in a frame (browser default instead of UTF-8)
    https://bugs.webkit.org/show_bug.cgi?id=197369

  3. Deprecate MediaType.APPLICATION_JSON_UTF8 in favor of APPLICATION_JSON
    https://github.com/spring-projects/spring-framework/issues/22788

  4. Upgrading to Spring Framework 5.x - Upgrading to Version 5.2
    https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x#upgrading-to-version-52

  5. What's New in Spring Framework 5.x - What's New in Version 5.2
    https://github.com/spring-projects/spring-framework/wiki/What's-New-in-Spring-Framework-5.x#whats-new-in-version-52

目次

  1. Spring Initializr で 2.2.2 のプロジェクトを作成する
  2. build.gradle を変更する
  3. build タスク実行時に出るエラーを修正する
  4. メモ書き

手順

Spring Initializr で 2.2.2 のプロジェクトを作成する

Spring Initializr で 2.2.2 のプロジェクトを作成して、生成された build.gradle を見て反映した方が良い点があるか確認します。

f:id:ksby:20191231105357p:plain f:id:ksby:20191231105449p:plain f:id:ksby:20191231114548p:plain f:id:ksby:20191231114639p:plain f:id:ksby:20191231114803p:plain f:id:ksby:20191231114839p:plain f:id:ksby:20191231114922p:plain f:id:ksby:20191231115024p:plain f:id:ksby:20191231115218p:plain f:id:ksby:20191231115303p:plain f:id:ksby:20191231115406p:plain

以下の build.gradle が作成されました。今回は Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その3 )( build.gradle を変更する ) で実施した時のような「Import Module from Gradle」ダイアログは表示されませんでした。

plugins {
    id 'org.springframework.boot' version '2.2.2.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    implementation 'org.springframework.boot:spring-boot-starter-freemarker'
    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.session:spring-session-data-redis'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'org.postgresql:postgresql'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.springframework.security:spring-security-test'
}

test {
    useJUnitPlatform()
}

以下の点はこれまで見たことがなかったので build.gradle に反映します。

  • configurations { ... } の記述が追加されている。
  • dependencies block で org.springframework.boot:spring-boot-devtools を指定する時の記述が developmentOnly になっている。
  • testImplementation('org.springframework.boot:spring-boot-starter-test')exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' が記述されている。

build.gradle を変更する

buildscript {
    ext {
        group "ksbysample"
        version "2.2.2-RELEASE"
    }
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/release/" }
        gradlePluginPortal()
    }
}

plugins {
    id "java"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.2.2.RELEASE"
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
    id "groovy"
    id "checkstyle"
//    id "com.github.spotbugs" version "1.6.9"
    id "pmd"
    id "net.ltgt.errorprone" version "0.7.1"
    id "com.gorylenko.gradle-git-properties" version "2.2.0"
}

..........

configurations {
    developmentOnly
    runtimeClasspath.extendsFrom developmentOnly
    compileOnly.extendsFrom annotationProcessor

    // annotationProcessor と testAnnotationProcessor、compileOnly と testCompileOnly を併記不要にする
    testAnnotationProcessor.extendsFrom annotationProcessor
    testImplementation.extendsFrom compileOnly

    // for Doma 2
    domaGenRuntime
}

..........

dependencyManagement {
    imports {
        // mavenBom は以下の URL のものを使用する
        // https://repo.spring.io/release/org/springframework/boot/spring-boot-starter-parent/2.2.2.RELEASE/
        // bomProperty に指定可能な property は以下の URL の BOM に記述がある
        // https://repo.spring.io/release/org/springframework/boot/spring-boot-dependencies/2.2.2.RELEASE/spring-boot-dependencies-2.2.2.RELEASE.pom
        mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) {
            // Spring Boot の BOM に定義されているバージョンから変更する場合には、ここに以下のように記述する
            // bomProperty "thymeleaf.version", "3.0.9.RELEASE"
        }
        mavenBom("org.junit:junit-bom:5.5.2")
    }
}

dependencies {
    def jdbcDriver = "org.postgresql:postgresql:42.2.9"
    def spockVersion = "1.3-groovy-2.5"
    def domaVersion = "2.26.0"
    def lombokVersion = "1.18.10"
    def errorproneVersion = "2.3.3"
    def powermockVersion = "2.0.4"
//    def spotbugsVersion = "3.1.11"

    // dependency-management-plugin によりバージョン番号が自動で設定されるもの
    // Appendix F. Dependency versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
    implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity5")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-freemarker")
    implementation("org.springframework.boot:spring-boot-starter-mail")
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-data-redis")
    implementation("org.springframework.boot:spring-boot-starter-amqp")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    compileOnly("org.springframework.boot:spring-boot-configuration-processor")
    implementation("org.springframework.session:spring-session-data-redis")
    implementation("org.springframework.retry:spring-retry")
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
    implementation("org.apache.commons:commons-lang3")
    implementation("org.codehaus.janino:janino")
    implementation("io.micrometer:micrometer-registry-prometheus")
    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude group: "org.junit.vintage", module: "junit-vintage-engine"
    }
    testImplementation("org.springframework.security:spring-security-test")
    testImplementation("org.yaml:snakeyaml")

    // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの
    runtimeOnly("${jdbcDriver}")
    implementation("com.integralblue:log4jdbc-spring-boot-starter:2.0.0")
    implementation("org.simpleframework:simple-xml:2.7.1")
    implementation("com.univocity:univocity-parsers:2.8.4")
    implementation("com.google.guava:guava:28.1-jre")
    implementation("org.flywaydb:flyway-core:6.1.3")
    testImplementation("org.dbunit:dbunit:2.6.0")
    testImplementation("com.icegreen:greenmail:1.5.11")
    testImplementation("org.assertj:assertj-core:3.14.0")
    testImplementation("com.jayway.jsonpath:json-path:2.4.0")
    testImplementation("org.jsoup:jsoup:1.12.1")
    testImplementation("cglib:cglib-nodep:3.3.0")
    testImplementation("org.spockframework:spock-core:${spockVersion}")
    testImplementation("org.spockframework:spock-spring:${spockVersion}")

    // for lombok
    // testAnnotationProcessor、testCompileOnly を併記しなくてよいよう configurations で設定している
    annotationProcessor("org.projectlombok:lombok:${lombokVersion}")
    compileOnly("org.projectlombok:lombok:${lombokVersion}")

    // for Doma
    annotationProcessor("org.seasar.doma:doma:${domaVersion}")
    implementation("org.seasar.doma:doma:${domaVersion}")
    domaGenRuntime("org.seasar.doma:doma-gen:${domaVersion}")
    domaGenRuntime("${jdbcDriver}")

    // for JUnit 5
    // junit-jupiter で junit-jupiter-api, junit-jupiter-params, junit-jupiter-engine の3つが依存関係に追加される
    testImplementation("org.junit.jupiter:junit-jupiter")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")

    // for Error Prone ( http://errorprone.info/ )
    errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}")
    compileOnly("com.google.errorprone:error_prone_annotations:${errorproneVersion}")

    // PowerMock
    testImplementation("org.powermock:powermock-module-junit4:${powermockVersion}")
    testImplementation("org.powermock:powermock-api-mockito2:${powermockVersion}")

    // for SpotBugs
//    compileOnly("com.github.spotbugs:spotbugs:${spotbugsVersion}")
//    compileOnly("net.jcip:jcip-annotations:1.0")
//    compileOnly("com.github.spotbugs:spotbugs-annotations:${spotbugsVersion}")
//    testImplementation("com.google.code.findbugs:jsr305:3.0.2")
}

Spring Boot 2.2.2 へのバージョンアップとして以下の点を変更します。

  • buildscript block の以下の点を変更します。
    • version "2.1.11-RELEASE"version "2.2.2-RELEASE"
  • plugins block の以下の点を変更します。
    • id "org.springframework.boot" version "2.1.11.RELEASE"id "org.springframework.boot" version "2.2.2.RELEASE"
  • configurations block に以下の3行を追加します。
    • developmentOnly
    • runtimeClasspath.extendsFrom developmentOnly
    • compileOnly.extendsFrom annotationProcessor
  • dependencies block の以下の点を変更します。
    • runtimeOnly("org.springframework.boot:spring-boot-devtools")developmentOnly("org.springframework.boot:spring-boot-devtools")
    • testImplementation("org.springframework.boot:spring-boot-starter-test")exclude group: "org.junit.vintage", module: "junit-vintage-engine" を追加します。

各種ライブラリのバージョンアップとして以下の点を変更します。

  • plugins block の以下の点を変更します。
    • id "com.gorylenko.gradle-git-properties" version "2.0.0"id "com.gorylenko.gradle-git-properties" version "2.2.0"
  • dependencyManagement の以下の点を変更します。
    • mavenBom("org.junit:junit-bom:5.4.1")mavenBom("org.junit:junit-bom:5.5.2")
  • dependencies block の以下の点を変更します。
    • def jdbcDriver = "org.postgresql:postgresql:42.2.5"def jdbcDriver = "org.postgresql:postgresql:42.2.9"
    • def domaVersion = "2.24.0"def domaVersion = "2.26.0"
    • def lombokVersion = "1.18.6"def lombokVersion = "1.18.10"
    • def powermockVersion = "2.0.0"def powermockVersion = "2.0.4"
    • implementation("com.integralblue:log4jdbc-spring-boot-starter:1.0.2")implementation("com.integralblue:log4jdbc-spring-boot-starter:2.0.0")
    • implementation("com.univocity:univocity-parsers:2.8.1")implementation("com.univocity:univocity-parsers:2.8.4")
    • implementation("com.google.guava:guava:27.1-jre")implementation("com.google.guava:guava:28.1-jre")
    • implementation("org.flywaydb:flyway-core:5.2.4")implementation("org.flywaydb:flyway-core:6.1.3")
    • testImplementation("com.icegreen:greenmail:1.5.10")testImplementation("com.icegreen:greenmail:1.5.11")
    • testImplementation("org.assertj:assertj-core:3.12.2")testImplementation("org.assertj:assertj-core:3.14.0")
    • testImplementation("org.jsoup:jsoup:1.11.3")testImplementation("org.jsoup:jsoup:1.12.1")
    • testImplementation("cglib:cglib-nodep:3.2.10")testImplementation("cglib:cglib-nodep:3.3.0")

build タスク実行時に出るエラーを修正する

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、build タスクでエラーが出るので修正します。

f:id:ksby:20191231135957p:plain

There were failing tests. See the report at: file:///D:/project-springboot/ksbysample-webapp-lending/build/reports/tests/test/index.html というメッセージが表示されたので、リンクをクリックして HTML をブラウザで表示させてみるとテストが2つ失敗していました。

f:id:ksby:20191231140102p:plain

失敗したログを表示させてみると、どちらも Content type expected:<application/json;charset=UTF-8> but was:<application/json> という理由でした。

f:id:ksby:20191231140320p:plain f:id:ksby:20191231140436p:plain

テストでは Content-Type に application/json;charset=UTF-8 がセットされてくる想定でいましたが、

f:id:ksby:20191231140644p:plain

@RestController が付加されたクラス内のメソッドの @RequestMappingvalue 属性しか指定していないと application/json;charset=UTF-8 ではなく application/json が返ってくるようになったようです。

f:id:ksby:20191231145744p:plain

org.springframework.http.MediaType クラスを見てみると、APPLICATION_JSON_UTF8、APPLICATION_JSON_UTF8_VALUE@Deprecated が付いていました。

f:id:ksby:20191231150817p:plain

Upgrading to Spring Framework 5.x - Upgrading to Version 5.2 にも Deprecation of MediaType.APPLICATION_JSON_UTF8 and MediaType.APPLICATION_PROBLEM_JSON_UTF8 の記述がありました。

よって、ksbysample.webapp.lending.webapi.library.LibraryControllerTest のテストクラス内で期待する Content-Type の文字列を application/json;charset=UTF-8application/json に変更することにします。

    @Test
    void 正しい都道府県を指定した場合には図書館一覧が返る() throws Exception {
        mvc.noauth.perform(get("/webapi/library/getLibraryList?pref=東京都"))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json"))
                .andExpect(jsonPath("$.errcode", is(0)))
                .andExpect(jsonPath("$.errmsg", is("")))
                .andExpect(jsonPath("$.content[0].address", startsWith("東京都")))
                .andExpect(jsonPath("$.content[?(@.formal=='国立国会図書館東京本館')]").exists());
    }

    @Test
    void 間違った都道府県を指定した場合にはエラーが返る() throws Exception {
        mvc.noauth.perform(get("/webapi/library/getLibraryList?pref=東a京都"))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json"))
                .andExpect(jsonPath("$.errcode", is(-2)))
                .andExpect(jsonPath("$.errmsg", is("都道府県名が正しくありません。")))
                .andExpect(jsonPath("$.content", hasSize(0)));
    }

application/json;charset=UTF-8 の件を変更した後、clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20191231155441p:plain

spring-boot-starter-test が JUnit 5 ベースに切り替わったのですが、Project Tool Window で src/test/groovy/ksbysample、src/test/java/ksbysample でコンテキストメニューを表示して「Run 'Tests in 'ksbysample'' with Coverage」を選択してテストを実行してもテストは全て成功しました。

f:id:ksby:20191231161711p:plain f:id:ksby:20191231162150p:plain

なんかあっさりバージョンアップできました。

メモ書き

履歴

2019/12/31
初版発行。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その3 )( Gradle を 5.6.4 → 6.0.1 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その2 )( Spring Boot を 2.1.4 → 2.1.11 へ、Gradle を 5.3.1 → 5.6.4 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Gradle を 5.6.4 → 6.0.1 へバージョンアップします。

参照したサイト・書籍

  1. Gradle 6.0 - Gradle Release Notes
    https://docs.gradle.org/6.0/release-notes.html

  2. Gradle 6.0.1 - Gradle Release Notes
    https://docs.gradle.org/6.0.1/release-notes.html

  3. Upgrading your build from Gradle 5.x to 6.0
    https://docs.gradle.org/current/userguide/upgrading_version_5.html

  4. gradle plugin doesn't seem to prefix jars by group ID on conflict
    https://github.com/spring-projects/spring-boot/issues/10778

目次

  1. Gradle を 5.6.4 → 6.0.1 にバージョンアップする
  2. Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0. の原因を調査する
  3. 他に build.gradle で変更した方がよい箇所を変更する

手順

Gradle を 5.6.4 → 6.0.1 にバージョンアップする

build.gradle の wrapper タスクの記述を以下のように変更します。

wrapper {
    gradleVersion = "6.0.1"
    distributionType = Wrapper.DistributionType.ALL
}
  • gradleVersion = "5.6.4"gradleVersion = "6.0.1" に変更します。

コマンドプロンプトから gradlew wrapper --gradle-version 6.0.1gradlew --version コマンドを実行します。

f:id:ksby:20191230213330p:plain

gradle/wrapper/gradle-wrapper.properties は以下の内容になります。

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

JVM を呼び出す時のメモリ割り当ての記述が元に戻るので、gradlew.bat 内の記述を set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"set DEFAULT_JVM_OPTS="-Xmx4096m" に変更します(gradlew も同じような変更をします)。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新すると、Could not create task ':spotbugsTest'.Could not create task ':spotbugsMain'. のエラーが出ました。

一旦 SpotBugs をコメントアウトします。build.gradle を以下のように変更します。

plugins {
    id "java"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.1.11.RELEASE"
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
    id "groovy"
    id "checkstyle"
//    id "com.github.spotbugs" version "1.6.9"
    id "pmd"
    id "net.ltgt.errorprone" version "0.7.1"
    id "de.undercouch.download" version "3.4.3"
    id "com.gorylenko.gradle-git-properties" version "2.0.0"
}

..........

//spotbugs {
//    toolVersion = "3.1.11"
//    ignoreFailures = true
//    effort = "max"
//    spotbugsTest.enabled = false
//}
//tasks.withType(com.github.spotbugs.SpotBugsTask) {
//    reports {
//        xml.enabled = false
//        html.enabled = true
//    }
//}

..........

dependencies {
    def jdbcDriver = "org.postgresql:postgresql:42.2.5"
    def spockVersion = "1.3-groovy-2.5"
    def domaVersion = "2.24.0"
    def lombokVersion = "1.18.6"
    def errorproneVersion = "2.3.3"
    def powermockVersion = "2.0.0"
//    def spotbugsVersion = "3.1.11"

    ..........

    // for SpotBugs
//    compileOnly("com.github.spotbugs:spotbugs:${spotbugsVersion}")
//    compileOnly("net.jcip:jcip-annotations:1.0")
//    compileOnly("com.github.spotbugs:spotbugs-annotations:${spotbugsVersion}")
//    testImplementation("com.google.code.findbugs:jsr305:3.0.2")
}

再び Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新すると、今度は正常に終了しました(少し時間がかかります)。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20191230220309p:plain

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0. の原因を調査する

build タスク実行時に以下のメッセージが出ていました。このメッセージが出ている原因を調べてみます。

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warnings

https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warnings を見ると gradlew build --warning-mode=all コマンドを実行すればメッセージが表示される原因が表示されるようです。

実行してみたところ、以下のメッセージが出力されました。

  • he testCompile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 7.0. Please use the testImplementation configuration instead.
    • testCompile で記述しているところが残っていたので testImplementation に変更します。Gradle 7.0 から古い記述は使用できなくなるようです。
  • The testRuntime configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 7.0. Please use the testRuntimeOnly configuration instead.
    • testRuntime で記述しているところが残っていたので testRuntimeOnly に変更します。
  • The baseName property has been deprecated. This is scheduled to be removed in Gradle 7.0. Please use the archiveBaseName property instead.
    • build.gradle に baseName の記述はないので、おそらく Plugin でしょう。現時点では無視します。
  • Property 'options.compilerArgumentProviders.errorprone$0.name' is not annotated with an input or output annotation. This behaviour has been deprecated and is scheduled to be removed in Gradle 7.0.
    • errorprone なので現時点では無視します。
  • Property 'gitProperties' is not annotated with an input or output annotation. This behaviour has been deprecated and is scheduled to be removed in Gradle 7.0.
    • build.gradle に baseName の記述はないので、おそらく Plugin でしょう。現時点では無視します。
    • com.gorylenko.gradle-git-properties が原因か?と思って 2.0.0 → 2.2.0 へバージョンアップしてみましたが、メッセージは消えませんでした。
  • Property 'source' is declared without normalization specified. Properties of cacheable work must declare their normalization via @PathSensitive, @Classpath or @CompileClasspath. Defaulting to PathSensitivity.ABSOLUTE. This behaviour has been deprecated and is scheduled to be removed in Gradle 7.0.
    • build.gradle に source の記述はないので、おそらく Plugin でしょう。現時点では無視します。
  • Copying or archiving duplicate paths with the default duplicates strategy has been deprecated. This is scheduled to be removed in Gradle 7.0. Duplicate path: "BOOT-INF/classes/META-INF/ksbysample/webapp/lending/dao/LendingAppDao/selectById.sql". Explicitly set the duplicates strategy to 'DuplicatesStrategy.INCLUDE' if you want to allow duplicate paths.
    • bootJar { rootSpec.duplicatesStrategy = DuplicatesStrategy.INCLUDE } の記述を追加すれば上のメッセージは出力されなくなりますが、おそらく Gradle 7.0 が正式リリースされるまでに Spring Boot の Plugin あたりで対応されると思うので現時点では無視します。

build.gradle の以下の点を変更します。

dependencies {
    ..........

    // for JUnit 5
    // junit-jupiter で junit-jupiter-api, junit-jupiter-params, junit-jupiter-engine の3つが依存関係に追加される
    testImplementation("org.junit.jupiter:junit-jupiter")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")

    ..........
}
  • dependencies block の以下の点を変更します。
    • testCompile("org.junit.jupiter:junit-jupiter")testImplementation("org.junit.jupiter:junit-jupiter")
    • testRuntime("org.junit.platform:junit-platform-launcher")testRuntimeOnly("org.junit.platform:junit-platform-launcher")

他に build.gradle で変更した方がよい箇所を変更する

buildscript {
    ext {
        group "ksbysample"
        version "2.1.11-RELEASE"
    }
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/release/" }
        gradlePluginPortal()
    }
}

plugins {
    id "java"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.1.11.RELEASE"
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
    id "groovy"
    id "checkstyle"
//    id "com.github.spotbugs" version "1.6.9"
    id "pmd"
    id "net.ltgt.errorprone" version "0.7.1"
    id "com.gorylenko.gradle-git-properties" version "2.0.0"
}

..........

// for Doma-Gen
task domaGen {
    doLast {
        // まず変更が必要なもの
        def rootPackageName = "ksbysample.webapp.lending"
        def daoPackagePath = "src/main/java/ksbysample/webapp/lending/dao"
        def dbUrl = "jdbc:postgresql://localhost/ksbylending"
        def dbUser = "ksbylending_user"
        def dbPassword = "xxxxxxxx"
        def tableNamePattern = ".*"
        // おそらく変更不要なもの
        def importOfComponentAndAutowiredDomaConfig = "${rootPackageName}.util.doma.ComponentAndAutowiredDomaConfig"
        def workDirPath = "work"
        def workDaoDirPath = "${workDirPath}/dao"

        // 作業用ディレクトリを削除する
        clearDir("${workDirPath}")

        // 現在の Dao インターフェースのバックアップを取得する
        copy() {
            from "${daoPackagePath}"
            into "${workDaoDirPath}/org"
        }

        // Dao インターフェース、Entity クラスを生成する
        ant.taskdef(resource: "domagentask.properties",
                classpath: configurations.domaGenRuntime.asPath)
        ant.gen(url: "${dbUrl}", user: "${dbUser}", password: "${dbPassword}", tableNamePattern: "${tableNamePattern}") {
            entityConfig(packageName: "${rootPackageName}.entity", useListener: false)
            daoConfig(packageName: "${rootPackageName}.dao")
            sqlConfig()
        }

        // 生成された Dao インターフェースを作業用ディレクトリにコピーし、
        // @ComponentAndAutowiredDomaConfig アノテーションを付加する
        copy() {
            from "${daoPackagePath}"
            into "${workDaoDirPath}/replace"
            filter {
                line ->
                    line.replaceAll("import org.seasar.doma.Dao;", "import ${importOfComponentAndAutowiredDomaConfig};\nimport org.seasar.doma.Dao;")
                            .replaceAll("@Dao", "@Dao\n@ComponentAndAutowiredDomaConfig")
            }
        }

        // @ComponentAndAutowiredDomaConfig アノテーションを付加した Dao インターフェースを
        // dao パッケージへ戻す
        copy() {
            from "${workDaoDirPath}/replace"
            into "${daoPackagePath}"
        }

        // 元々 dao パッケージ内にあったファイルを元に戻す
        copy() {
            from "${workDaoDirPath}/org"
            into "${daoPackagePath}"
        }

        // 作業用ディレクトリを削除する
        delete "${workDirPath}"
    }
}
  • buildscript block の以下の点を変更します。
    • maven { url "https://plugins.gradle.org/m2/" }gradlePluginPortal()
  • Gradle 7.0 から大きく仕様が変わるようなので build.gradle から不要になった記述を削除します。
    • 「メソッド定義部」の下に記述している関数の内、compareSrcAndTestDir 以外は削除します。
    • domaGen タスクの以下の点を変更します。
      • clearDir("${workDirPath}")delete "${workDirPath}" に変更します。
      • addGit() を削除します。
    • downloadCssFontsJs タスクを削除します。
    • plugins block から id "de.undercouch.download" version "3.4.3" を削除します。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新した後、clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20191231085410p:plain

履歴

2019/12/31
初版発行。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その2 )( Spring Boot を 2.1.4 → 2.1.11 へ、Gradle を 5.3.1 → 5.6.4 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その1 )( 概要 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Spring Boot のバージョンを 2.1 系の最新バージョンである 2.1.11 へ、Gradle のバージョンを 5.x 系の最新バージョンである 5.6.4 に上げて build できることを確認します。
    • 今回は問題がなければライブラリはバージョンアップしません。

参照したサイト・書籍

目次

  1. 2.2.x ブランチの作成
  2. Spring Boot を 2.1.4 → 2.1.11 にバージョンアップする
  3. Gradle を 5.3.1 → 5.6.4 にバージョンアップする

手順

2.2.x ブランチの作成

master から 2.2.x ブランチを、2.2.x から feature/134-issue ブランチを作成します。

Spring Boot を 2.1.4 → 2.1.11 にバージョンアップする

build.gradle の以下の点を変更します。

buildscript {
    ext {
        group "ksbysample"
        version "2.1.11-RELEASE"
    }
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/release/" }
        maven { url "https://plugins.gradle.org/m2/" }
    }
}

plugins {
    id "java"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.1.11.RELEASE"
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
    id "groovy"
    id "checkstyle"
    id "com.github.spotbugs" version "1.6.9"
    id "pmd"
    id "net.ltgt.errorprone" version "0.7.1"
    id "de.undercouch.download" version "3.4.3"
    id "com.gorylenko.gradle-git-properties" version "2.0.0"
}
  • buildscript block の以下の点を変更します。
    • version "2.1.4-RELEASE"version "2.1.11-RELEASE"
  • plugins block の以下の点を変更します。
    • id "org.springframework.boot" version "2.1.4.RELEASE"id "org.springframework.boot" version "2.1.11.RELEASE"
    • id "io.spring.dependency-management" version "1.0.7.RELEASE"id "io.spring.dependency-management" version "1.0.8.RELEASE"

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新した後、clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20191230195533p:plain

Gradle を 5.3.1 → 5.6.4 にバージョンアップする

build.gradle の wrapper タスクの記述を以下のように変更します。

wrapper {
    gradleVersion = "5.6.4"
    distributionType = Wrapper.DistributionType.ALL
}
  • gradleVersion = "5.3.1"gradleVersion = "5.6.4" に変更します。

コマンドプロンプトから gradlew wrapper --gradle-version 5.6.4gradlew --version コマンドを実行します。

f:id:ksby:20191230200400p:plain

gradle/wrapper/gradle-wrapper.properties は以下の内容になります。

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

JVM を呼び出す時のメモリ割り当ての記述が元に戻るので、gradlew.bat 内の記述を set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"set DEFAULT_JVM_OPTS="-Xmx4096m" に変更します(gradlew も同じような変更をします)。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します(少し時間がかかります)。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、compileJava タスクで [DOMA4019] The file "META-INF/ksbysample/webapp/lending/dao/LendingAppDao/selectById.sql" is not found in the classpath. The absolute path is "D:\project-springboot\ksbysample-webapp-lending\build\classes\java\main\META-INF\ksbysample\webapp\lending\dao\LendingAppDao\selectById.sql". の DOMA4019 のエラーが出ました。

f:id:ksby:20191230204432p:plain

Doma 2 のマニュアルの Build with Gradle と今の build.gradle の設定が違っていることは以前から認識していたので、Doma 2 のマニュアルに設定を合わせることにします。

build.gradle の以下の点を変更します。

[compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8"
[compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = ["-Xlint:all,-options,-processing,-path"]
tasks.named("compileTestJava").configure {
    options.errorprone.enabled = false
}

// for Doma 2
// Copy the resources referred by the Doma annotation processors to
// the destinationDir of the compileJava task
task copyDomaResources(type: Sync)  {
    from sourceSets.main.resources.srcDirs
    into compileJava.destinationDir
    include "doma.compile.config"
    include "META-INF/**/*.sql"
    include "META-INF/**/*.script"
}
compileJava.dependsOn copyDomaResources

springBoot {
    buildInfo()
}
  • 以下の記述を削除します。
    • processResources.destinationDir = compileJava.destinationDir
    • compileJava.dependsOn processResources
  • copyDomaResources タスクを追加します。
  • compileJava.dependsOn copyDomaResources を追加します。

再び clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、compileJava タスクは正常に終了しましたが、今度は pmdMain タスクで No such property: context for class: org.gradle.internal.logging.slf4j.DefaultContextAwareTaskLogger のエラーが表示されました。

build.gradle からエラーになった以下の記述を削除します。

pmdMain {
    def backupLoggerLevel
    doFirst {
        backupLoggerLevel = logger.context.level
        logger.context.level = LogLevel.QUIET
    }
    doLast {
        logger.context.level = backupLoggerLevel
    }
}

再び clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20191230210415p:plain

履歴

2019/12/30
初版発行。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その1 )( 概要 )

概要

記事一覧はこちらです。

  • 「Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る」で作成した Web アプリケーション ( ksbysample-webapp-lending ) の Spring Boot のバージョンを 2.1.4 → 2.2.x へバージョンアップします。
  • 進め方は以下の方針とします。
    • Git のブランチは 2.2.x を作成して、そちらで作業します。Spring Boot のバージョンと合わせます。
    • Spring Boot のバージョンを 2.1 系の最新バージョンである 2.1.11 へ、Gradle のバージョンを 5.x 系の最新バージョンである 5.6.4 に上げて build できることを確認します。この時点ではライブラリはバージョンアップしません。
    • Gradle のバージョンを 5.3.1 → 6.x へバージョンアップします。
    • Spring Boot のバージョン番号を 2.2.x にします。
      • Spring Initializr で 2.2.x のプロジェクトを作成して、修正した方がよさそうな点があれば反映します。
      • ライブラリは最新バージョンにアップデートします。ただし、この時点では checkstyle, spotbugs, pmd, Error Prone のバージョンは上げません。
    • プロジェクトを build し直してエラーが出る点があれば修正し、まずはここまでで動くようにします。
    • その後で 2.2 系ではこう書くべきという点があるか確認し、変更した方がよいところを変更します。
    • checkstyle, spotbugs, pmd, Error Prone を1つずつ最新バージョンに上げます。変更した方がよいところがあれば変更します。  
       

2.2 の Release Notes はこちらです。

Spring Boot 2.2 Release Notes
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.2-Release-Notes

履歴

2019/12/30
初版発行。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( 大目次 )

  1. その1 ( 概要 )
  2. その2 ( Spring Boot を 2.1.4 → 2.1.11 へ、Gradle を 5.3.1 → 5.6.4 へバージョンアップする )
  3. その3 ( Gradle を 5.6.4 → 6.0.1 へバージョンアップする )
  4. その4 ( Spring Boot を 2.1.11 → 2.2.2 へバージョンアップする )
  5. その5 ( Release Notes を見て必要な箇所を変更する )
  6. その6 ( Checkstyle を 8.19 → 8.28 へバージョンアップする )
  7. その7 ( PMD を 6.13.0 → 6.20.0 へバージョンアップする )
  8. その8 ( Error Prone を 2.3.3 → 2.3.4 へバージョンアップする )
  9. その9 ( SpotBugs を 3.1.11 → 4.0.0-beta4 へバージョンアップする )
  10. その10 ( SpotBugs プラグインの findsecbugs-plugin を導入する )
  11. その11 ( Docker コンテナの image をバージョンアップする )
  12. その12 ( spring.main.lazy-initialization を試してみる )
  13. 番外編 ( IntelliJ IDEA の画面上の文字化けを解消する )
  14. 番外編 ( IntelliJ IDEA の Grazie・GitToolBox プラグインを追加する )
  15. 番外編 ( IntelliJ IDEA の Translation・Redis プラグインを追加する )
  16. その13 ( gradle の VM オプションを gradle.proeprties に移行する+spring.main.lazy-initialization を試してみる2 )
  17. その14 ( Docker で複数の Tomcat を起動して動作確認する )
  18. 感想