SpringBootでRestTemplateの設定を変えたい
- Spring Boot 2.5.2
本稿の対象
以下のような人が対象です。
- SpringBootを使っていて
new RestTemplate()
とかを書いた/見たことがある。 - SpringBootを使っていて
new RestTemplateBuilder()
とかを書いた/見たことがある。 - ぼんやり知ってるけど仕組みとかおさえておきたい。
- SpringBootのドキュメント/コードの読み方の一例を知りたい。
特に new
してるのを書いたり見たことある人に読んでほしい。悲しい事故が起こる前に。
以下のような人は対象ではありません。
- SpringBootのドキュメントとコードを必要な時に読んでいる。
RestTemplate
を使っているがSpringBootを使っていない。- SpringBootで
RestTemplate
を使っているが、restTemplateBuilder.build()
しか書いた/見たことがない。
SpringFrameworkはドキュメント全体的にしっかりしているんだけど、コードとあわせて読まないといまいち意味がわからなかったりします(私は)。それでいいんだっけ?とか不安になっちゃうし、「なんでこれで動くん?」みたいに疑心暗鬼になることもしばしば。ということで「 RestTemplate
の設定の仕方」を題材にコードのこの辺見てますよ、みたいなことを書こうと思いました。何気に主題はこちらだったりします。
本文
公式ドキュメントに RestTemplate Customization ってストレートな項目があるんで、これ読みましょう。
……で終わるならいいんですが、書いてる内容を咀嚼しながら適当なこと書きますね。
RestTemplate
は spring-web
に入ってる大昔(SpringFramework 3.0らしい)からあるとても便利なクラスです。
SpringFramework 5.0からメンテナンスモードに入り、ノンブロッキングの流れも受けて WebClient
に徐々にうつっていくとは思いますが、現時点では 決して非推奨ではありません。@Deprecated
とかもついてない。たまに「もう使ってはいけない」とかいう話を聞いたりするので強調しておきます。
というか、まだ WebClient
だと厳しいことにも引っかかったりするんで、WebFluxを使ってるとか必要性があったり、チャレンジして何か踏んでも何とかできる方でもなければ RestTemplate
を使うのが無難です。そんな安定志向な私。他の人がどう判断するかは知らない。
さて本題の RestTemplate
の設定変更ですが、ドキュメントで示されているのは以下。
「なるべく狭い範囲でカスタマイズしましょう」
共通の設定は RestTemplate
全てに影響しちゃうから、「ほんとに全部?特定の通信先に固有の設定をしたいんじゃないの?」みたいな感じですかね。
大前提として
SpringBootを使ってる文脈で new RestTemplate()
とかするのは基本的に無しです。1年ちょい前にmakingさんがツイートしてたのに続けてはなしてたこと の説明が本稿な感じです。
new
しちゃうと「SpringBootだからこれでいけるだろー」みたいなのが軒並み通らなくなって、よくわからないことになります。RestTemplate
のインスタンスは RestTemplateBuilder
を build
して作るものです。その RestTemplateBuilder
も自分で new
するんじゃなく、SpringBootが管理してるインスタンスを貰ってくるものです。基本から外れるものもたまにあるけど、それはそれ。
ということで、基本のお話だー
まず build
の直前
最初に検討するのは RestTemplateBuilder
を build
する箇所。
要するにコンストラクタで RestTemplateBuilder
をインジェクションした後ですね。これでやってる人が多いんじゃないかな?
RestTemplateBuilder
の何かしらのメソッド呼ぶたびに新しい RestTemplateBuilder
のインスタンスが返されるから他のRestTemplate
に影響しない。安全。
コードを見たら 、thisじゃなく新しいインスタンスがreturnされてるのがわかりますね。
次に RestTemplateCustomizer
RestTemplateCustomizer
を実装してBean登録する。
RestTemplateBuilder
のフィールドに(RestTemplateBuilderConfigurer
経由で)インジェクションされて、build
するときに勝手に呼ばれる。
なのですべてのRestTemplateBuilder
から作るRestTemplate
に必ず適用される感じ。
- RestTemplateBuilderのcustomizersフィールド
- build直前に呼び出されるconfigureメソッドの適用される箇所。
restTemplate
をreturn
する直前に入ってますね。
なんとなく RestTemplateCustomizer
はあまり知られてない気がする。ライブラリとかでは結構使われるんですが。
RestTemplateCustomizer
って単純にRestTemplate
を受け取るだけのConsumerなFunctionalInterfaceなんですよね。
@FunctionalInterface public interface RestTemplateCustomizer { /** * Callback to customize a {@link RestTemplate} instance. * @param restTemplate the template to customize */ void customize(RestTemplate restTemplate); }
GitHub: spring-projects/spring-boot/.../RestTemplateCustomizer.java
実務だと「RestTemplate
を受け取って設定を行う共通のユーティリティメソッドを忘れずに呼び出す」とか規約作るくらいならこれBean登録しとこう、とかかなー。
RestTemplateCustomizer
は名前の示す通りRestTemplate
が対象の拡張ポイントです。 RestTemplateBuilder
に対してではないって把握しておかないと「あれ、これでできないの?」とかなったり、たまにある。ということでRestTemplateBuilder
に共通的に何かを行いたい時には使えません。次の手段で。
最後の手段として RestTemplateBuilder
自体をBean登録
RestTemplateBuilder
はSpringBoot使っているならおなじみ(?)の @ConditionalOnMissingBean
な「何もしなければ spring-boot-autoconfigure
が勝手に作成してくれるもの」です。定義はこう。
@Bean @Lazy @ConditionalOnMissingBean public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) { RestTemplateBuilder builder = new RestTemplateBuilder(); return restTemplateBuilderConfigurer.configure(builder); }
GitHub: spring-projects/spring-boot-autoconfigure/.../RestTemplateAutoConfiguration.java
@ConditionalOnMissingBean
は名前の通り「この型のBeanが登録されていない場合のみ有効になる」ものなので、自分でRestTemplateBuilder
を登録したらこれが使われません。
つまり 迂闊にやるとSpringBootの制御を外れた RestTemplateBuilder
を作ってしまう ことになります。
ドキュメントにも「Finally」と最後に挙げてるくらい、やって欲しくなさげ。
で、迂闊じゃないやり方は元と同じように RestTemplateBuilderConfigurer
を使用することです。ドキュメントにも書いてる。これを使わないと以下のような問題が起こります。
まず RestTemplateCustomizer
がBean登録されてても効きません。
例えば @AutoConfigureMockRestServiceServer
が RestTemplate
にバインドされす、
Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate
とか顔を真っ赤にして(ログが赤い的な意味で)怒られてしまいます。エラーログに丁寧に書いてくれるのほんとありがたいんだけど、早口な英語で捲し立てられてる気分でちょっとビクッとしちゃったりしますよね。スタックトレース怖い。こわくないよ。 これ MockServerRestTemplateCustomizer で実現されてるからですね。
あと TraceRestTemplateCustomizer とかも効かなくなるので、せっかく spring-cloud-sleuth
とか入れてても分散トレーシングが途切れます。一番大事なとこで。。。
他にもHttpMessageConverters
も入らないからJacksonの spring.jackson.*
とか設定してても効かなくなる。「あれー application.yml
にちゃんと書いてるのになー」とかなる、くらいならいいんだけど(よくないけど)、メッセージ変換周りがまるごと spring-boot
じゃなく spring-web
の素 RestTemplate
になってしまう。結構違いあるんで注意なとこです。
さらに RestTemplateRequestCustomizer
も入らない。実装したことないし、心当たりある例も思いつかないけど。
今のところ RestTemplateBuilderConfigurer
はこの3種類しか扱っていませんが、今後バージョンアップで増えたり変わったりするかもしれない。となると「バージョンあげたらうまく動かなくなった」とかの禍根になります。JavaやSpringFrameworkの互換維持ってかなりすごいんだけど、こういうの積み重ねちゃうとバージョンアップの手を緩めさせてしまいます。これが怖い。
やるべきことはシンプルで、元のと同じように RestTemplateBuilder
のインスタンスを作った後に RestTemplateBuilderConfigurer#configure(RestTemplateBuilder)
を被せてあげるだけ。
@ConditionalOnMissingBean
とかの spring-boot-autoconfigure
のBeanを上書きするときは「だいたい元の実装を真似る」ってやっとくのが無難です。ドキュメントに書いてはいるんだけど、それのやってることを文章だけで読み取るのは結構難度高いと思う。
この「最後に」とかで挙げられてる RestTemplateBuilder
のBean登録をいつ使うかっていうと、アプリケーション全体で RestTemplate
じゃなく RestTemplateBuilder
で設定したいときです。たとえばタイムアウトの設定とかは RestTemplate
経由だと RequestFactory
(の実装クラス)をいじらないといけないけど、RestTemplateBuilder
だと直接的なメソッドがあったりするんですよね。
前に書いたこれとか。だけどタイムアウトって通信相手ごとに設定する気もするし、コンストラクタのbuild直前、つまり最初に挙げられてた選択肢でいい気もしなくはない。 RequestFactory
もいじるならこっちかなーって思ってる。 RestTemplateRequestCustomizer
でも手が届くけど、一旦作ったのを上書きって感じになるし、なんとなく無駄に感じるので……気分の問題だけど。
まぁここで書いたような事故もあるし、RestTemplateRequestCustomizer
で事足りるなら RestTemplateRequestCustomizer
でやっとく方が安全でしょね。という意味で、三つの方法の中では最後になってるのかなと。
most extreme (and rarely used) option
最後の最後に書いてる。
The most extreme (and rarely used) option is to create your own RestTemplateBuilder bean without using a configurer.
一応 RestTemplateCustomizer
使わないって選択肢も挙げられてるけど、「何でそんなことすんの・・・?」みたいな感じを受けますね。
SpringBootから RestTemplateBuilder
を切り離したいなら……でもそれなら RestTemplateBuilder
を使わずに new RestTemplate()
でいい気がするけども。
自分の制御下にない RestTemplateBuilder
を使っているコードに影響を与えたいなら、かなぁ……。
でも「RestTemplateを設定したい」と言いつつ
実際のとこ RestTemplate
の設定をしたいと言いつつ、 MessageConverter
を設定したいことの方が多い気がします。
そういう時に下手にこの辺に書いてあるように RestTemplateBuilder
や RestTemplateCustomizer
で MessageConverter
を丸ごと入れ替えたりすると、先にあげたような「Jacksonの設定が効かない!」とか、なりがちです。
設定を変えたいものはどのライブラリの管轄で、それがどういう仕組みで直接使うAPI(RestTemplate
とか)と連携しているかを把握できれば、ここのエントリで書いたように楽勝です。……それがハードル高いんだってーの。
SpringBootとJSPと
JSPはすごくいい技術なんだけど、使い所も減ってきて最近はあまり見なくなってきました。
Spring Boot(2.5.2)のドキュメントを見てるとこんな感じ。
7.7.1. The “Spring Web MVC Framework” / Template Engines
Spring MVC supports a variety of templating technologies, including Thymeleaf, FreeMarker, and JSPs.
「サポートしてるよ!」と言いつつ一番最後。
If possible, JSPs should be avoided.
悪いことは言わないから避けた方がいいよ?ってさ。
7.7.4. Embedded Servlet Container Support / JSP Limitations
With Jetty and Tomcat, it should work if you use war packaging.
JettyやTomcatならwarにしたら多分動くよ、とのこと。なんとなく「JettyやTomcatが動かすはず。知らんけど。」くらいの印象を受けました。「知らんけど」よりもうちょっと責任感ありそうだけど、動かすのは自分じゃないって主張してる感ある。
「Spring Bootのつもりで作っててもたまに動かない制限ある」って感じだし、やっぱSpringBoot+JSPの新規採用は避けるのが無難。まぁ新規でわざわざJSPにしたがるのは多分ないでしょうけど、この辺り検討するのは既存のJSPを引き継いだり拡張したりって文脈だろうし、「使うな」なんて言っても現実見ようねって話になりがち。使わない方向に押し切った方がいいことも多いだろけど、いちがいにはいえない、ようはばらんす。バランスなんで天秤に載せる錘にどんなものがあるかは知っておきたいし、どれを使うかは現場で見極めていきたいところ。まぁJSP自体は悪い子じゃないんだよ。。。
えいご
英語のニュアンス、いまいちよくわからないんだけど、英英辞書ひいたりコアイメージ見たら「あーね」ってなることもよくあります。そいや最近図解してくれてるのを買いました。
多分電子より物理の方がいいです。手頃なサイズだし。説明も私みたいな雰囲気人間にはちょうどよくて、shouldはこんな感じ。
めっちゃ弱いんよなぁ。学生時代shouldは強制くらいに覚えた記憶あるんだけど、若干弱いのを知ったときは混乱したりも。shouldとかこの辺の使い分けは RFCのも役に立ちますね。英語ドキュメント読むときはほぼ必須。。
さーばーさいどれんだりんぐ
Reactとかフロントはフロントで作ってAPIでごにょごにょってのが多分今は優勢で、JSPのようなサーバーサイドでHTML書き出すのは若干古いアプローチと捉えられたりするかもしれませんが、これも結局使い所なんだよなぁ、と思ったり。 UIのリッチさとかセキュリティとか性能とか、いろんなパラメータがあるんで、その辺を見ながら丁寧に仕事していきたいと思ってる。
どうでもいいけどReact5回くらい完全に理解したのに、未だまったく理解できない。困ったもんだ。。。
蛇足
そいやJakarta Server Pagesを「JSP」って呼び続けていいのかはよくわからないですが、ダメと言われたところでどうせそう呼ばれるんだろうなぁ、って。
年中税金的なものを払ってる気がする
多いと思ったら1年の12ヶ月中6ヶ月は何かしら払ってるんですね。打率5割。そりゃ多いわ。
- 4月
- 国民年金保険料。20万円。
- 5月
- 所得税。所得に応じて。確定申告の締め切り延期でちょっとズレてる。
- 消費税。売上の10%あればいい。確定申告の締め切り延期でちょっとズレてる。
- 6月
- 住民税。所得の1/10あったら多分足りる。タイミングは住んでる場所による。
- 国民健康保険料。80万円超えてきた。
- 7月
- 所得税の予定納税。5月と同じくらいと思っていればまぁ。
- 8月
- 個人事業税。8月と11月だけど忘れるから2回分まとめて。所得の5%?
- 11月
- 所得税の予定納税。5月と同じくらいと思っていればまぁ。
なんか微妙に計算合わない気がするけど、まぁこんなもんでしょう…… 固定資産税とかないから、その辺ある人はもう少し増える?(よくわかってない
個人事業主なんで税金的なものを自分で払う必要があり、引き落とされたり振り込んだりする時に口座にお金がないとだめなんですよね。必要なタイミングでお金をおいとかないと振替失敗とか考えるだけでも面倒そうです。 いついくら持っていかれるか把握してないので迂闊に口座を空にできず、そこそこの額を遊ばせてるわけでして。 なんでいい加減整理してみよう、と思い立ってのこれです。
住民税と国民健康保険料は自動振替してない、かつ金額的にコンビニとかキャッシュレスとか使えないので銀行に行かねばならなくて。 6月は同じなんだけど用紙届くタイミングが微妙に違って、去年とか同じ月に2回銀行いくことになってとても面倒くさかった。
全部まとめて一回で持ってってくれないものかなぁ。。