かんがるーさんの日記

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

Spring Boot でログイン画面 + 一覧画面 + 登録画面の Webアプリケーションを作る ( その24 )( 登録画面のドロップダウンリストを設定ファイルから取得する方法の変更 )

概要

Spring Boot でログイン画面 + 一覧画面 + 登録画面の Webアプリケーションを作る ( その23 )( logback.xmlの設定 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。

    • 登録画面の Continent のドロップダウンリストに表示するリストを設定ファイル constant.properties から読み込むようにしたかったので、CountryController クラス内で return "country/input"; の前に model.addAttribute("continentList", constant.CONTINENT_LIST); を呼び出してテンプレートファイルに設定ファイルのデータを渡すようにしていましたが、model.addAttribute を呼び出さなくてもよい方法に変更します。
  • 登録画面を表示する時に毎回 model.addAttribute を呼び出すのはスマートな方法ではないなあと思いつつも、それ以外の方法が分からなかったため、とりあえず動けばよいと考えて実装していましたが、毎回 model.addAttribute を呼び出さなくてもよい方法が見つかりましたので変更します。

ソフトウェア一覧

参考にしたサイト

  1. stackoverflow - Access from Thymeleaf to class field
    http://stackoverflow.com/questions/25120799/access-from-thymeleaf-to-class-field

    • Thymeleaf のテンプレートファイルから static フィールドの値を参照する方法が書かれています。

手順

登録画面のドロップダウンリストを設定ファイルから取得する方法の変更

  1. IntelliJ IDEA 上で 1.0.x-chgoptiontagvalue ブランチを作成します。

  2. src/main/java/ksbysample/webapp/basic/config の下の Constant.javaリンク先の内容 に変更します。

  3. src/main/resources/templates/country の下の input.html を リンク先の内容 に変更します。

  4. src/main/java/ksbysample/webapp/basic/web の下の CountryController.javaリンク先の内容 に変更します。

  5. 動作確認します。bootRun タスクを実行し、Tomcat を起動します。

  6. ブラウザからログインして登録画面を表示し、Continent のドロップダウンリストに値が表示されることを確認します。

    f:id:ksby:20150329171752p:plain

  7. Run View で Ctrl+F2 を押して Tomcat を停止します。

  8. commit の前に build タスクを実行し、BUILD SUCCESSFUL が表示されることを確認します。

  9. commit、GitHub へ Push、1.0.x-chgoptiontagvalue -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-chgoptiontagvalue ブランチを削除、をします。

考察

  • 今回の実装をしていて、@Component アノテーションをクラスに付加すると Tomcat 起動時にクラスのインスタンスが生成されることに気づきました。ということは @Component アノテーションが付いたクラスが増えると Tomcat の起動時間が増えるってことですよね。。。 起動時ではなく最初にアクセスされた時に生成してくれるオプションがあるといいのですが、そういうものはないのでしょうか?

ソースコード

Constant.java

@Component
@PropertySource("classpath:constant.properties")
public class Constant {

    public static List<String> CONTINENT_LIST;

    @Autowired
    private Constant(
            @Value("#{'${CONTINENT_LIST}'.split(',')}") List<String> CONTINENT_LIST
    ) {
        this.CONTINENT_LIST = CONTINENT_LIST;
    }

}
  • public final List<String> CONTINENT_LIST;public static List<String> CONTINENT_LIST; へ変更します。final 修飾子も付けたかったのですが、付けるとエラーになりました ( コンストラクタで初期化しようとしているのがダメなようです ) 。
  • このクラスは static フィールド参照専用にするので、コンストラクタのアクセス修飾子を public Constant {private Constant { へ変更し、インスタンス化できないようにします。
  • クラスに @Component アノテーションを、コンストラクタ@Autowired アノテーションを付けたままにします。こうすることで、Tomcat 起動時にコンストラクタが実行されて設定ファイルの値が CONTINENT_LIST にセットされるようになります。

input.html

                                <select name="continent" id="continent" class="form-control input-sm" th:field="*{continent}">
                                    <option th:each="item : ${T(ksbysample.webapp.basic.config.Constant).CONTINENT_LIST}"
                                            th:value="${item}"
                                            th:text="${item}">continent</option>
                                </select>
  • <option th:each="item : ${continentList}"<option th:each="item : ${T(ksbysample.webapp.basic.config.Constant).CONTINENT_LIST}" へ変更します。ドロップダウンリストに表示する値を Constant クラスの static フィールドから直接取得するようにします。

CountryController.java

package ksbysample.webapp.basic.web;

import ksbysample.webapp.basic.domain.Country;
import ksbysample.webapp.basic.exception.InvalidRequestException;
import ksbysample.webapp.basic.service.CountryRepository;
import ksbysample.webapp.basic.service.CountryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;

@Controller
@RequestMapping("/country")
public class CountryController {

    @Autowired
    private CountryFormValidator countryFormValidator;

    @Autowired
    private CountryService countryService;

    @Autowired
    private CountryRepository countryRepository;

    @Autowired
    private MessageSource messageSource;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.addValidators(countryFormValidator);
    }

    @RequestMapping("/input")
    public String input(CountryForm countryForm
            , Model model) {

        return "country/input";
    }

    @RequestMapping("/input/back")
    public String inputBack(CountryForm countryForm
            , RedirectAttributes redirectAttributes) {

        redirectAttributes.addFlashAttribute("countryForm", countryForm);
        return "redirect:/country/input";
    }

    @RequestMapping("/confirm")
    public String confirm(@Validated CountryForm countryForm
            , BindingResult bindingResult
            , Model model) {

        if (bindingResult.hasErrors()) {
            return "country/input";
        }

        // code に入力された文字列をキーに持つデータが登録されている場合にはエラーにする
        Country country = countryRepository.findOne(countryForm.getCode());
        if (country != null) {
            bindingResult.reject("countryForm.global.duplicate");
            return "country/input";
        }

        return "country/confirm";
    }

    @RequestMapping("/update")
    public String update(@Validated CountryForm countryForm
            , BindingResult bindingResult
            , Locale locale
            , Model model
            , HttpServletResponse response) throws IOException, InvalidRequestException {

        if (bindingResult.hasErrors()) {
            throw new InvalidRequestException(messageSource.getMessage("common.invalidRequestException.message", new Object[]{bindingResult.toString()}, locale));
        }

        countryService.save(countryForm);
        return "redirect:/country/complete";
    }

    @RequestMapping("/complete")
    public String complete() {
        return "country/complete";
    }

}
  • private Constant constant; を削除します。
  • model.addAttribute("continentList", constant.CONTINENT_LIST); を書いているところを全て削除します。

履歴

2015/03/29
初版発行。