Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その9 )( 1.3系 → 1.4系で実装方法が変更された点を修正する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- 1.3系 → 1.4系で実装方法が変更された点を修正します。
- 今回は変更が軽微なものだけで、フィールドへの @Autowired 付加 → コンストラクタインジェクションへの変更、及びテストクラスのアノテーションの変更は次回以降にやります。
参照したサイト・書籍
-
Spring Boot 1.4 Release Notes
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes Spring Boot 1.4+でRestTemplate(HTTPクライアント)を使う
http://qiita.com/kazuki43zoo/items/7cf3c8ca4f6f2283cefb
目次
- spring-boot-starter で名前が変更されたものを反映する
- application.properties に spring.session.store-type の設定を追加する
- application.properties の spring.datasource の項目名を変更する
- @RequestMapping を変更可能なところは @GetMapping, @PostMapping に変更する
- RestTemplate オブジェクトを生成する処理を
new RestTemplate(...)
→RestTemplateBuilder#build
へ変更する - 次回は。。。
手順
spring-boot-starter で名前が変更されたものを反映する
1.4 から spring-boot-starter-redis
→ spring-boot-starter-data-redis
へ変更されたので反映します。
build.gradle を リンク先の内容 に変更します。
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
Project Tool Window の External Libraries を見ると
spring-boot-starter-redis
が消えてspring-boot-starter-data-redis
が入ったことが確認できます。
application.properties に spring.session.store-type の設定を追加する
Spring Session のデータ保存先を application.properties で設定できるようになったので、その設定を追加します。
application.properties を リンク先の内容 に変更します。
設定する時に IntelliJ IDEA で候補を表示させてみたのですが、
hash_map
やnone
といった選択肢もありました。一時的に Spring Session を試したいだけならサーバを立てずにhash_map
を選択するのもありなのかもしれません。none
は何だろう?と思ったら、39. Spring Session に Spring Session を無効化したい時に設定するよう記述がありました。
application.properties の spring.datasource の項目名を変更する
使用しているコネクションプーリングのライブラリに応じた namespace が増えました。Spring Boot では特に指定をしていなければ Tomcat JDBC Connection Pool が使用されるので、spring.datasource
→ spring.datasource.tomcat
へ変更します。
Appendix A. Common application properties には「spring.datasource.tomcat.*= # Tomcat datasource specific settings」としか記述がありませんが、IntelliJ IDEA で設定項目を表示させてみたところ、以下の項目が表示されました。
知らない設定がいろいろ表示されています。次回以降で設定しておいた方がよいものがあるのか調べてみたいと思います。Tomcat JDBC Connection Pool の URL もメモしておきます。
The Tomcat JDBC Connection Pool
https://tomcat.apache.org/tomcat-8.5-doc/jdbc-pool.html
application-develop.properties, application-product.properties, application-unittest.properties を リンク先の内容 に変更します。
src/main/java/ksbysample/webapp/lending/config の下の ApplicationConfig.java を リンク先のその1の内容 に変更します。
※org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration クラスが dataSource Bean を生成して spring.datasource.tomcat の設定を反映してくれると思ったのですが、なぜかうまく行きませんでした。。。 ので、自分で dataSource Bean を定義しています。
@RequestMapping を変更可能なところは @GetMapping, @PostMapping に変更する
ksbysample-webapp-lending のソースを見てみると @RequestMapping(value = "...", method = RequestMethod.POST)
のように POST のみに制限しているところはあっても、GET のみで制限しているところはありませんでした。今回は @RequestMapping(value = "...", method = RequestMethod.POST)
→ @PostMapping("...")
にのみ変更することにします。
以下のクラスのメソッドで
@RequestMapping(value = "...", method = RequestMethod.POST)
→@PostMapping("...")
へ変更します。- ksbysample.webapp.lending.web.confirmresult.ConfirmresultController#filedownloadByResponse
- ksbysample.webapp.lending.web.confirmresult.ConfirmresultController#filedownloadByView
- ksbysample.webapp.lending.web.lendingapp.LendingappController#apply
- ksbysample.webapp.lending.web.lendingapp.LendingappController#temporarySave
- ksbysample.webapp.lending.web.lendingapproval.LendingapprovalController#complete
RestTemplate オブジェクトを生成する処理を new RestTemplate(...)
→ RestTemplateBuilder#build
へ変更する
RestTemplate オブジェクトを生成するための RestTemplateBuilder クラスが提供されています。タイムアウトを設定するのも RestTemplateBuilder クラスを使用した方が簡単で分かりやすいので、RestTemplateBuilder クラスを利用する方法に変更します。
MappingJackson2XmlHttpMessageConverter を生成する部分は Bean にします。src/main/java/ksbysample/webapp/lending/config の下の ApplicationConfig.java を リンク先のその2の内容 に変更します。
src/main/java/ksbysample/webapp/lending/service/calilapi の下の CalilApiService.java を リンク先の内容 に変更します。
※src/main/java/ksbysample/webapp/lending/service/openweathermapapi の下の OpenWeatherMapApiService.java は使用していないのとテストも動かないので、変更しません。
次回は。。。
引き続き 1.3系 → 1.4系で実装方法が変更された点を修正します。
次回はフィールドへの @Autowired 付加 → コンストラクタインジェクションへの変更、その次にテストクラスのアノテーションの変更をやる予定です。
ソースコード
build.gradle
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Appendix A. Dependency versions ( http://docs.spring.io/platform/docs/current/reference/htmlsingle/#appendix-dependency-versions ) 参照 .......... compile("org.springframework.boot:spring-boot-starter-security") compile("org.springframework.boot:spring-boot-starter-data-redis") compile("org.springframework.boot:spring-boot-starter-amqp") ..........
spring-boot-starter-redis
→spring-boot-starter-data-redis
へ変更します。
application.properties
hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect doma.dialect=org.seasar.doma.jdbc.dialect.PostgresDialect spring.jpa.hibernate.ddl-auto=none spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy spring.session.store-type=redis spring.freemarker.cache=true spring.freemarker.charset=UTF-8 spring.freemarker.enabled=false spring.freemarker.prefer-file-system-access=false
spring.session.store-type=redis
を追加します。
application-develop.properties, application-product.properties, application-unittest.properties
■application-develop.properties
spring.datasource.tomcat.url=jdbc:log4jdbc:postgresql://localhost/ksbylending spring.datasource.tomcat.username=ksbylending_user spring.datasource.tomcat.password=xxxxxxxx spring.datasource.tomcat.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
■application-product.properties
spring.datasource.tomcat.url=jdbc:postgresql://localhost/ksbylending spring.datasource.tomcat.username=ksbylending_user spring.datasource.tomcat.password=xxxxxxxx spring.datasource.tomcat.driverClassName=org.postgresql.Driver
■application-unittest.properties
spring.datasource.tomcat.url=jdbc:postgresql://localhost/ksbylending spring.datasource.tomcat.username=ksbylending_user spring.datasource.tomcat.password=xxxxxxxx spring.datasource.tomcat.driverClassName=org.postgresql.Driver
- 全て
spring.datasource
→spring.datasource.tomcat
へ変更します。尚、上で設定している url, username, password, driverClassName のような全てのコネクションプーリングで共通の設定の場合にはspring.datasource
のままでも構いません。
ApplicationConfig.java
■その1
.......... import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; .......... import javax.sql.DataSource; @Configuration public class ApplicationConfig { .......... /** * @return Tomcat JDBC Connection Pool の DataSource オブジェクト */ @Bean @ConfigurationProperties("spring.datasource.tomcat") public DataSource dataSource() { return DataSourceBuilder.create() .type(org.apache.tomcat.jdbc.pool.DataSource.class) .build(); } }
- dataSource メソッドを追加します。この時
@ConfigurationProperties("spring.datasource.tomcat")
アノテーションを付加してspring.datasource.tomcat
の設定が DataSource オブジェクトに設定されるようにします。
■その2
@Configuration public class ApplicationConfig { .......... /** * 外部のWebAPIとXMLフォーマットで通信するために使用する MessageConverter * build.gralde に compile("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:...") を記述して * jackson-dataformat-xml が使用できるように設定しないと Bean は生成されない * * @return MappingJackson2XmlHttpMessageConverter オブジェクト */ @Bean @ConditionalOnClass(com.fasterxml.jackson.dataformat.xml.XmlMapper.class) public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter() { // findAndRegisterModules メソッドを呼び出して jackson-dataformat-xml が機能するようにする return new MappingJackson2XmlHttpMessageConverter(new XmlMapper().findAndRegisterModules()); } }
- mappingJackson2XmlHttpMessageConverter Bean を追加します。
- 以前 ksbysample.webapp.lending.service.calilapi.CalilApiService#getMessageConvertersforJackson2Xml に実装していた内容を持ってきて、以下の点を変更しました。
assert(...)
は削除して@ConditionalOnClass(com.fasterxml.jackson.dataformat.xml.XmlMapper.class)
に変更しました。- MappingJackson2XmlHttpMessageConverter#setObjectMapper を呼び出すのではなく、
new MappingJackson2XmlHttpMessageConverter(...)
の引数で渡すように変更しました。 - spring-framework/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java を見ると、デフォルトで必要な MediaType がセットされていたので、MappingJackson2XmlHttpMessageConverter#setSupportedMediaTypes でセットしていた処理を削除しました。
- 以前 ksbysample.webapp.lending.service.calilapi.CalilApiService#getMessageConvertersforJackson2Xml に実装していた内容を持ってきて、以下の点を変更しました。
CalilApiService.java
package ksbysample.webapp.lending.service.calilapi; import com.google.common.base.Joiner; import ksbysample.webapp.lending.service.calilapi.response.Book; import ksbysample.webapp.lending.service.calilapi.response.CheckApiResponse; import ksbysample.webapp.lending.service.calilapi.response.Libraries; import ksbysample.webapp.lending.service.calilapi.response.LibrariesForJackson2Xml; import org.simpleframework.xml.Serializer; import org.simpleframework.xml.core.Persister; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; import java.util.Map; @Service @PropertySource("classpath:calilapi.properties") public class CalilApiService { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final int RETRY_MAX_CNT = 5; private static final long RETRY_SLEEP_MILLS = 3000; private static final String URL_CALILAPI_ROOT = "http://api.calil.jp"; private static final String URL_CALILAPI_LIBRALY = URL_CALILAPI_ROOT + "/library?appkey={appkey}&pref={pref}"; private static final String URL_CALILAPI_CHECK = URL_CALILAPI_ROOT + "/check?appkey={appkey}&systemid={systemid}&isbn={isbn}&format=xml"; private static final String URL_CALILAPI_CHECK_FOR_RETRY = URL_CALILAPI_ROOT + "/check?session={session}&format=xml"; @Value("${calil.apikey}") private String calilApiKey; private final RestTemplate restTemplateForCalilApi; private final RestTemplate restTemplateForCalilApiByXml; public CalilApiService(@Qualifier("restTemplateForCalilApi") RestTemplate restTemplateForCalilApi , @Qualifier("restTemplateForCalilApiByXml") RestTemplate restTemplateForCalilApiByXml) { this.restTemplateForCalilApi = restTemplateForCalilApi; this.restTemplateForCalilApiByXml = restTemplateForCalilApiByXml; } /** * @param pref ??? * @return ??? * @throws Exception */ public Libraries getLibraryList(String pref) throws Exception { // 図書館データベースAPIを呼び出して XMLレスポンスを受信する ResponseEntity<String> response = this.restTemplateForCalilApi.getForEntity(URL_CALILAPI_LIBRALY , String.class, this.calilApiKey, pref); // 受信した XMLレスポンスを Javaオブジェクトに変換する Serializer serializer = new Persister(); Libraries libraries = serializer.read(Libraries.class, response.getBody()); return libraries; } /** * @param pref ??? * @return ??? * @throws Exception */ public LibrariesForJackson2Xml getLibraryListByJackson2Xml(String pref) throws Exception { // 図書館データベースAPIを呼び出して XMLレスポンスを受信する ResponseEntity<LibrariesForJackson2Xml> response = this.restTemplateForCalilApiByXml.getForEntity(URL_CALILAPI_LIBRALY , LibrariesForJackson2Xml.class, this.calilApiKey, pref); return response.getBody(); } /** * @param systemid ??? * @param isbnList ??? * @return ??? */ public List<Book> check(String systemid, List<String> isbnList) { Map<String, String> vars = new HashMap<>(); vars.put("appkey", this.calilApiKey); vars.put("systemid", systemid); vars.put("isbn", Joiner.on(",").join(isbnList)); ResponseEntity<CheckApiResponse> response = null; String url = URL_CALILAPI_CHECK; for (int retry = 0; retry < RETRY_MAX_CNT; retry++) { // 蔵書検索APIを呼び出して蔵書の有無と貸出状況を取得する response = this.restTemplateForCalilApiByXml.getForEntity(url, CheckApiResponse.class, vars); logger.info("カーリルの蔵書検索API を呼び出し、レスポンスを取得しました。{}", response.getBody().toString()); if (response.getBody().getContinueValue() == 0) { break; } // continue の値が 0 でない場合には2秒以上待機した後、URLパラメータを session に変更して再度リクエストを送信する try { Thread.sleep(RETRY_SLEEP_MILLS); } catch (InterruptedException e) { logger.warn("カーリルの蔵書検索APIのsleep中にInterruptedExceptionが発生しましたが、処理は継続します。", e); } url = URL_CALILAPI_CHECK_FOR_RETRY; vars.clear(); vars.put("session", response.getBody().getSession()); } return response.getBody().getBookList(); } @Configuration public static class CalilApiConfig { private static int CONNECT_TIMEOUT = 5000; private static int READ_TIMEOUT = 5000; private final RestTemplateBuilder restTemplateBuilder; private final MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter; /** * コンストラクタ * * @param restTemplateBuilder restTemplateBuilder Bean * @param mappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter Bean */ public CalilApiConfig(RestTemplateBuilder restTemplateBuilder , MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter) { this.restTemplateBuilder = restTemplateBuilder; this.mappingJackson2XmlHttpMessageConverter = mappingJackson2XmlHttpMessageConverter; } /** * カーリルの図書館API呼び出し用 RestTemplate * JSON フォーマットで結果を受信する * * @return RestTemplate オブジェクト */ @Bean public RestTemplate restTemplateForCalilApi() { return this.restTemplateBuilder .setConnectTimeout(CONNECT_TIMEOUT) .setReadTimeout(READ_TIMEOUT) .rootUri(URL_CALILAPI_ROOT) .build(); } /** * カーリルの図書館API呼び出し用 RestTemplate * XML フォーマットで結果を受信する * * @return RestTemplate オブジェクト */ @Bean public RestTemplate restTemplateForCalilApiByXml() { return this.restTemplateBuilder .setConnectTimeout(CONNECT_TIMEOUT) .setReadTimeout(READ_TIMEOUT) .rootUri(URL_CALILAPI_ROOT) .messageConverters(this.mappingJackson2XmlHttpMessageConverter) .build(); } } }
@Configuration public static class CalilApiConfig { ... }
を追加して、その中で restTemplateForCalilApi Bean, restTemplateForCalilApiByXml Bean を定義するようにします。- RETRY_MAX_CNT, RETRY_SLEEP_MILLS の定義は CalilApiConfig クラス内へ移動します。
- URL_CALILAPI_ROOT の定義を追加し、URL_CALILAPI_LIBRALY, URL_CALILAPI_CHECK, URL_CALILAPI_CHECK_FOR_RETRY の URL の定義を URL_CALILAPI_ROOT を使うように変更しました。URL_CALILAPI_ROOT は restTemplateForCalilApi Bean, restTemplateForCalilApiByXml Bean の生成時に RestTemplateBuilder#rootUri を呼び出す時に使用します。
- DI 用の以下のフィールドと、インジェクションするためのコンストラクタを追加します。
private final RestTemplate restTemplateForCalilApi;
private final RestTemplate restTemplateForCalilApiByXml;
- 各メソッド内で
new RestTemplate(...)
を呼び出して RestTemplate オブジェクトを生成している処理を削除しました。
履歴
2017/02/22
初版発行。