SlideShare ist ein Scribd-Unternehmen logo
1 von 189
Downloaden Sie, um offline zu lesen
徹底解剖
Spring MVCアーキテクチャー
-DispatcherServletの中身を覗いてきました-
株式会社カサレアル
菊池 真登
2019/12/18
SPRING FEST ❜19
(C) CASAREAL, Inc. All rights reserved. 1
突然ですが
(C) CASAREAL, Inc. All rights reserved. 2
@Controller
public class TestController {
@PostMapping("/test")
public String post(@Validated TestForm testForm,
BindingResult result,
Model model) {
return "test";
}
}
ごくごく一般的なコントローラー
の実装ですよね!?
気になったことはありませんか?
(C) CASAREAL, Inc. All rights reserved. 3
@Controller
public class TestController {
@PostMapping("/test")
public String post(@Validated TestForm testForm,
BindingResult result,
Model model) {
return "test";
}
}
リクエストURLと
メソッドの関係は?
引数のインスタンス
はどこからくる?
戻り値って文字列以外でもOK?
気になったことはありませんか?
(C) CASAREAL, Inc. All rights reserved. 4
@Controller
public class TestController {
@PostMapping("/test")
public String post(@Validated TestForm testForm,
BindingResult result,
Model model) {
return "test";
}
}
引数に設定可能な
型って何個ある?
@ValidatedとBindingResult以外って
順番決まりなさそうだけど。。。
くっ、黒魔術!?
(C) CASAREAL, Inc. All rights reserved. 5
このセッションは
(C) CASAREAL, Inc. All rights reserved. 6
Spring MVCのアーキテクチャーを
ソースコードリーディングをもとに解き明か
していく45分間です
Spring MVCのアーキテクチャーをより詳しく
知りたい方が対象です
内部構造を詳細に把握することでトラブルに
強くなれます
自己紹介:菊池真登
(C) CASAREAL, Inc. All rights reserved. 7
 研修トレーナー@カサレアル
 登壇実績:Spring / Java SE
 開発歴[Spring]:2012年から
 開発歴[Angular]:2015年頃に
1.5年間AngularJSに触れて冬眠
株式会社カサレアル
(C) CASAREAL, Inc. All rights reserved. 8
他社にはない色々なプログラミング言語の
研修を提供しています!
目次
(C) CASAREAL, Inc. All rights reserved. 9
1.Spring MVC のアーキテクチャー
2.URLとコントローラーメソッド
3.コントローラーメソッドの引数
4.コントローラーメソッドの戻り値
5.全体を俯瞰
目次
(C) CASAREAL, Inc. All rights reserved. 10
1.Spring MVC のアーキテクチャー
2.URLとコントローラーメソッド
3.コントローラーメソッドの引数
4.コントローラーメソッドの戻り値
5.全体を俯瞰
Spring MVC のアーキテクチャー
(C) CASAREAL, Inc. All rights reserved. 11
Dispatcher
Servlet
Handler
Mapping
Handler
Adapter
View
Resolver Model
Controller
view name
View
1
2 3
4
56
関係を詳しく見ると
(C) CASAREAL, Inc. All rights reserved. 12
Dispatcher
Servlet DIコンテナ
Handler
Mapping
Handler
Adapter
DispatcherServletはDIコンテナをもつ
そのDIコンテナがHandlerMappingや
HandlerAdapterなどをもつ
処理実行時は
(C) CASAREAL, Inc. All rights reserved. 13
Dispatcher
Servlet
Handler
Adapter
Handler
Mapping
DispatcherServletの初期化処理で
DIコンテナからインスタンスを取り出し
フィールドに直接保持する
DispatcherServlet#initHandlerMappings() - 一部
(C) CASAREAL, Inc. All rights reserved. 14
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
インスタンスを取り出し、
フィールド変数に代入
HandlerMapping
(C) CASAREAL, Inc. All rights reserved. 15
Dispatcher
Servlet
Handler
Mapping
Controller
1
リクエストURLとコントローラー
クラスのメソッドとの関係とは?
HandlerAdapter – 実行前
(C) CASAREAL, Inc. All rights reserved. 16
Dispatcher
Servlet
Handler
Adapter
Controller
2 3
コントローラークラスのメソッド
の引数はどのようにして解決?
HandlerAdapter – 実行後
(C) CASAREAL, Inc. All rights reserved. 17
Dispatcher
Servlet
Handler
Adapter
Controller
view name
2 3
4
コントローラークラスのメソッドの
戻り値はどのように処理される?
目次
(C) CASAREAL, Inc. All rights reserved. 18
1.Spring MVC のアーキテクチャー
2.URLとコントローラーメソッド
3.コントローラーメソッドの引数
4.コントローラーメソッドの戻り値
5.全体を俯瞰
Spring MVC のアーキテクチャー
(C) CASAREAL, Inc. All rights reserved. 19
Dispatcher
Servlet
Handler
Mapping
Handler
Adapter
View
Resolver Model
Controller
view name
View
1
2 3
4
56
HandlerMapping
(C) CASAREAL, Inc. All rights reserved. 20
Dispatcher
Servlet
Handler
Mapping
Controller
1
リクエストURLとコントローラー
クラスのメソッドとの関係とは?
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 21
 HandlerMapping
 RequestMappingInfo
 MappingRegistry
 HandlerMethod
 AbstractHandlerMethodMapping
ざっくりとした関係図
(C) CASAREAL, Inc. All rights reserved. 22
HandlerMapping
MappingRegistry
RequestMappingInfo
RequestMappingInfo
RequestMappingInfo
HandlerMethod
HandlerMethod
HandlerMethod
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 23
 HandlerMapping
 RequestMappingInfo
 MappingRegistry
 HandlerMethod
 AbstractHandlerMethodMapping
関係図
(C) CASAREAL, Inc. All rights reserved. 24
HandlerMapping
HandlerMapping
(C) CASAREAL, Inc. All rights reserved. 25
 インタフェース
 getHandlerメソッドが一つだけ定義されている
 戻り値が現在のリクエストを処理するメソッドになる
HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception;
RequestMappingHandlerMapping
(C) CASAREAL, Inc. All rights reserved. 26
 HandlerMappingを実装したクラス
 コントローラークラス用
 @EnableWebMvcアノテーションで有効化
 Spring Bootではアノテーションは不要
 一般的には下記の説明
Bean定義されているContorllerから
@RequestMappingアノテーションを読み取り、
URLと合致するControllerのメソッドを探す
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 27
 HandlerMapping
 RequestMappingInfo
 MappingRegistry
 HandlerMethod
 AbstractHandlerMethodMapping
HandlerMapping
関係図
(C) CASAREAL, Inc. All rights reserved. 28
RequestMappingInfo
RequestMappingInfo
RequestMappingInfo
RequestMappingInfo
(C) CASAREAL, Inc. All rights reserved. 29
 @RequestMappingの設定値を保持する
 コントローラーメソッドの定義情報を表すクラス
 コントローラーメソッドの数だけ作られる
 1メソッド : 1Info
 アプリ実行中はMappingRegistryに保持される
RequestMappingInfo
(C) CASAREAL, Inc. All rights reserved. 30
public final class RequestMappingInfo implements
RequestCondition<RequestMappingInfo> {
@Nullable
private final String name;
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
@RequestMappingの
value:URL
method:リクエストメソッド
の設定値を保持する
RequestMappingInfo 続き
(C) CASAREAL, Inc. All rights reserved. 31
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
params
headers
consumes
produces
の設定値を
保持する
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 32
 HandlerMapping
 RequestMappingInfo
 MappingRegistry
 HandlerMethod
 AbstractHandlerMethodMapping
関係図
(C) CASAREAL, Inc. All rights reserved. 33
HandlerMapping
MappingRegistry
RequestMappingInfo
RequestMappingInfo
RequestMappingInfo
MappingRegistry
(C) CASAREAL, Inc. All rights reserved. 34
 リクエストURLとRequestMappingInfoを紐づける
 AbstractHandlerMethodMapping内にあるクラス
 アクセス修飾子なしのクラス
 実行中はAbstractHandlerMethodMappingのフィールドに
インスタンスが保持される
MappingRegistry
(C) CASAREAL, Inc. All rights reserved. 35
class MappingRegistry {
private final Map<RequestMappingInfo,HandlerMethod> mappingLookup;
private final MultiValueMap<String, RequestMappingInfo > urlLookup;
@RequestMappingアノテーションのvalue属性すなわち
URL を key にして、 RequestMappingInfo が保持される
RequestMappingInfo を key に HandlerMethod が保持される
*見やすさのため、実際のソースコードとは異なります
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 36
 HandlerMapping
 RequestMappingInfo
 MappingRegistry
 HandlerMethod
 AbstractHandlerMethodMapping
関係図
(C) CASAREAL, Inc. All rights reserved. 37
HandlerMapping
MappingRegistry
RequestMappingInfo
RequestMappingInfo
RequestMappingInfo
HandlerMethod
HandlerMethod
HandlerMethod
HandlerMethod
(C) CASAREAL, Inc. All rights reserved. 38
 コントローラーメソッドの化身
 メソッドのリフレクションをもつ
 フレームワーク内部でコントローラメソッドを
取り扱うためのもの
ここで、
RequestMappingInfoとHandlerMethod
の関係を整理しましょう
(C) CASAREAL, Inc. All rights reserved. 39
【例】コントローラークラスの定義
(C) CASAREAL, Inc. All rights reserved. 40
@Controller
public class TestController {
@GetMapping("/")
public String index() { return "index"; }
@GetMapping("/test")
public String get() { return "get"; }
@PostMapping("/test")
public String post() { return "post"; }
}
・URL:”/”、メソッド: GET
・URL:”/test”、メソッド GET
・URL:”/test”、メソッド POST
の3つを定義する
MappingRegistryのurlLookupの状態
(C) CASAREAL, Inc. All rights reserved. 41
“/”
“/test”
Patterns:”/”
Methods:”GET”
Patterns:”/test”
Methods:”GET”
Patterns:”/test”
Methods:”POST”
key : URL value : RequestMappingInfo
MappingRegistryのmappingLookupの状態
(C) CASAREAL, Inc. All rights reserved. 42
Patterns:”/”
Methods:”GET”
Patterns:”/test”
Methods:”GET”
Patterns:”/test”
Methods:”POST”
key : RequestMappingInfo value : HandlerMethod
TestController#index()
TestController#get()
TestController#post()
まとめると
(C) CASAREAL, Inc. All rights reserved. 43
Patterns:”/”
Methods:”GET”
Patterns:”/test”
Methods:”GET”
Patterns:”/test”
Methods:”POST”
TestController#index()
TestController#get()
TestController#post()
“/”
“/test”
URL RequestMappingInfo HandlerMethod
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 44
 HandlerMapping
 RequestMappingInfo
 MappingRegistry
 HandlerMethod
 AbstractHandlerMethodMapping
継承関係
(C) CASAREAL, Inc. All rights reserved. 45
RequestMappingHandlerMapping
AbstractHandlerMethodMapping
HandlerMapping
AbstractHandlerMethodMapping
(C) CASAREAL, Inc. All rights reserved. 46
 HandlerMappingの処理の根幹
 RequestMappingHandlerMappingが継承
 lookupHandlerMethodメソッドが肝!
ざっくりいうと下記の二段構成
1. リクエストURLと完全一致するものを探す
2. なければ全検索でパターンマッチを行う
リクエストURLと完全一致するものを探す
(C) CASAREAL, Inc. All rights reserved. 47
AbstractHandlerMethodMapping#lookupHandlerMethod()
(C) CASAREAL, Inc. All rights reserved. 48
protected HandlerMethod lookupHandlerMethod(String lookupPath,
HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches
= this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
リクエストURLをKeyに
RequestMappingInfoを取り出す
MappingRegistry#getMappingsByUrl()
(C) CASAREAL, Inc. All rights reserved. 49
class MappingRegistry {
private final MultiValueMap<String, RequestMappingInfo> urlLookup
public List<RequestMappingInfo> getMappingsByUrl(String urlPath) {
return this.urlLookup.get(urlPath);
}
【例】リクエストURLが / の場合
(C) CASAREAL, Inc. All rights reserved. 50
“/”
Patterns:”/”
Methods:”GET”
このRequestMappingInfo
が選択される
key : URL value : RequestMappingInfo
【例】リクエストURLが /test の場合
(C) CASAREAL, Inc. All rights reserved. 51
“/test”
Patterns:”/test”
Methods:”GET”
Patterns:”/test”
Methods:”POST”
これらのRequestMappingInfo
が選択される
key : URL value : RequestMappingInfo
リクエストメソッドなど細かい判定を行う
(C) CASAREAL, Inc. All rights reserved. 52
 リクエストメソッド等細かな条件で絞り込む
 RequestMappingInfo#getMatchingCondition()で行う
 @RequestMappingのすべての属性値が判定対象
 method
 params
 headers
 consumes
 produces
AbstractHandlerMethodMapping#lookupHandlerMethod()
(C) CASAREAL, Inc. All rights reserved. 53
protected HandlerMethod lookupHandlerMethod(String lookupPath,
HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<RequestMappingInfo> directPathMatches
= this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
リクエストメソッドなどの
より細かな精査を行う
RequestMappingInfo#getMatchingCondition()
(C) CASAREAL, Inc. All rights reserved. 54
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods
= this.methodsCondition.getMatchingCondition(request);
if (methods == null) { // リクエストメソッドの判定
return null;
}
ParamsRequestCondition params
= this.paramsCondition.getMatchingCondition(request);
if (params == null) { // リクエストパラメータの判定
return null;
}
先ほどのaddMatchingMappings
メソッドの呼び出しで
最終的に呼ばれるメソッド
getMatchingCondition - 続き
(C) CASAREAL, Inc. All rights reserved. 55
HeadersRequestCondition headers
= this.headersCondition.getMatchingCondition(request);
if (headers == null) { // リクエストヘッダーの判定
return null;
}
ConsumesRequestCondition consumes
= this.consumesCondition.getMatchingCondition(request);
if (consumes == null) { // Content-Typeの判定
return null;
}
getMatchingCondition - 続き
(C) CASAREAL, Inc. All rights reserved. 56
ProducesRequestCondition produces
= this.producesCondition.getMatchingCondition(request);
if (produces == null) { // Acceptの判定
return null;
}
PatternsRequestCondition patterns
= this.patternsCondition.getMatchingCondition(request);
if (patterns == null) { // リクエストURLのパターン判定
return null;
}
getMatchingCondition - 続き
(C) CASAREAL, Inc. All rights reserved. 57
RequestConditionHolder custom
= this.customConditionHolder.getMatchingCondition(request);
if (custom == null) { // カスタム判定
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces,
custom.getCondition());
}
(C) CASAREAL, Inc. All rights reserved. 58
なければ全検索でパターンマッチを行う
@RequestMappingにワイルドカード
を指定した場合など
(C) CASAREAL, Inc. All rights reserved. 59
(C) CASAREAL, Inc. All rights reserved. 60
protected HandlerMethod lookupHandlerMethod(String lookupPath,
HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches
= this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(
this.mappingRegistry.getMappings().keySet(), matches, request);
}
全RequestMappingInfoから
パターンマッチを行う
AbstractHandlerMethodMapping#lookupHandlerMethod()
【例】コントローラークラスの定義
(C) CASAREAL, Inc. All rights reserved. 61
@Controller
public class TestController {
@GetMapping("/**")
public String index() { return "index"; }
@GetMapping("/test")
public String get() { return "get"; }
}
・URL:”/**”、メソッド: GET
・URL:”/test”、メソッド GET
の2つを定義する
MappingRegistryのurlLookupの状態
(C) CASAREAL, Inc. All rights reserved. 62
“/**”
“/test”
Patterns:”/**”
Methods:”GET”
Patterns:”/test”
Methods:”GET”
key : URL value : RequestMappingInfo
MappingRegistryのmappingLookupの状態
(C) CASAREAL, Inc. All rights reserved. 63
Patterns:”/**”
Methods:”GET”
Patterns:”/test”
Methods:”GET”
TestController#index()
TestController#get()
key : RequestMappingInfo value : HandlerMethod
リクエストURL : “/” メソッド : “GET”
(C) CASAREAL, Inc. All rights reserved. 64
Patterns:”/**”
Methods:”GET”
Patterns:”/test”
Methods:”GET”
TestController#index()
TestController#get()
“/**”
“/test”
リクエストURL : /
は完全一致しない
全RequestMappingInfo
からパターンマッチ
を行う
パターンマッチは前述の
RequestMappingInfo#getMatchingCondition()
を使う
(C) CASAREAL, Inc. All rights reserved. 65
RequestMappingInfo#getMatchingCondition() – 再掲
(C) CASAREAL, Inc. All rights reserved. 66
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods
= this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
// 中略
PatternsRequestCondition patterns
= this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
@GetMapping("/**")
メソッド : GET、にマッチ
@GetMapping("/**")
URL : /**、にマッチ
【例】コントローラークラスの定義
(C) CASAREAL, Inc. All rights reserved. 67
@Controller
public class TestController {
@GetMapping("/**")
public String index() { return "index"; }
@GetMapping("/test")
public String get() { return "get"; }
}
ゆえに、indexメソッドが選択
される
(C) CASAREAL, Inc. All rights reserved. 68
このようにして選択されたHandlerMethodを
HandlerAdapterが実行する
(C) CASAREAL, Inc. All rights reserved. 69
目次
(C) CASAREAL, Inc. All rights reserved. 70
1.Spring MVC のアーキテクチャー
2.URLとコントローラーメソッド
3.コントローラーメソッドの引数
4.コントローラーメソッドの戻り値
5.全体を俯瞰
Spring MVC のアーキテクチャー
(C) CASAREAL, Inc. All rights reserved. 71
Dispatcher
Servlet
Handler
Mapping
Handler
Adapter
View
Resolver Model
Controller
view name
View
1
2 3
4
56
Handler Adapter – 実行前
(C) CASAREAL, Inc. All rights reserved. 72
Dispatcher
Servlet
Handler
Adapter
Controller
2 3
コントローラークラスのメソッド
の引数はどのようにして解決?
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 73
 HandlerMethodArgumentResolver
 HandlerAdapter
 ServletInvocableHandlerMethod
関係図
(C) CASAREAL, Inc. All rights reserved. 74
HandlerAdapter
HandlerMethodArgumentResolver
ServletInvocableHandlerMethod
(リクエストごとに作られる)
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 75
 HandlerMethodArgumentResolver
 HandlerAdapter
 ServletInvocableHandlerMethod
関係図
(C) CASAREAL, Inc. All rights reserved. 76
HandlerMethodArgumentResolver
HandlerMethodArgumentResolver
(C) CASAREAL, Inc. All rights reserved. 77
 コントローラーメソッドの引数の調達役
 インタフェースであり、これを多数のクラスが実装
 Form
 BindingResult
 Servlet API
など、各クラス分実装クラスが存在する
 Spring SecurityやSpring Data Commons 等にも実装クラス
が存在する
 メソッドで UserDetails 等を取得するのもこの機能
HandlerMethodArgumentResolver
(C) CASAREAL, Inc. All rights reserved. 78
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory)
throws Exception;
}
HandlerMethodArgumentResolver
(C) CASAREAL, Inc. All rights reserved. 79
 supportsParameter()
 メソッド引数の型や付加したアノテーションなどか
らサポート対象かを判定するメソッド
 resolveArgument()
 メソッド引数のインスタンスを調達するメソッド
以後、ArgumentResolver と省略した場合は、
こちらを示しています。
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 80
 HandlerMethodArgumentResolver
 HandlerAdapter
 ServletInvocableHandlerMethod
関係図
(C) CASAREAL, Inc. All rights reserved. 81
HandlerAdapter
HandlerMethodArgumentResolver
HandlerAdapter
(C) CASAREAL, Inc. All rights reserved. 82
 HandlerMappingで選択されたHandlerMethodを実行する
 インタフェース
 handle()メソッド内で HandlerMethod を実行する
RequestMappingHandlerAdapter
(C) CASAREAL, Inc. All rights reserved. 83
 @RequestMappingで定義されたHandlerMethodを
サポートするHandlerAdapterの実装クラス
 HandlerMethodArgumentResolverのインスタンスを保持
 ServletInvocableHandlerMethodに
HandlerMethodArgumentResolverのインスタンスを
渡して委譲する
継承関係
(C) CASAREAL, Inc. All rights reserved. 84
RequestMappingHandlerAdapter
HandlerAdapter
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 85
 HandlerMethodArgumentResolver
 HandlerAdapter
 ServletInvocableHandlerMethod
関係図
(C) CASAREAL, Inc. All rights reserved. 86
HandlerAdapter
HandlerMethodArgumentResolver
ServletInvocableHandlerMethod
(リクエストごとに作られる)
ServletInvocableHandlerMethod
(C) CASAREAL, Inc. All rights reserved. 87
 HandlerMethodを継承している
 RequestMappingHandlerAdapterから借り受けた
HandlerMethodArgumentResolverを使って
引数のインスタンスを調達する
 コントローラーメソッドを実行する
継承関係
(C) CASAREAL, Inc. All rights reserved. 88
ServletInvocableHandlerMethod
HandlerMethod
関係図
(C) CASAREAL, Inc. All rights reserved. 89
RequestMapping
HandlerAdapter
ServletInvocable
HandlerMethod
リクエストごとに
new
this.argumentResolvers
setHandlerMethodArgumentResolvers
これ使って! 任せとけ!
InvocableHandlerMethod#invokeForRequest()
(C) CASAREAL, Inc. All rights reserved. 90
public Object invokeForRequest(NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args
= getMethodArgumentValues(request,
mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
引数のインスタンス
を調達
メソッドの実行!
InvocableHandlerMethod#getMethodArgumentValues ()
(C) CASAREAL, Inc. All rights reserved. 91
protected Object[] getMethodArgumentValues(
NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
引数なしの場合は
そのままreturn
引数リストを取得
InvocableHandlerMethod#getMethodArgumentValues()
(C) CASAREAL, Inc. All rights reserved. 92
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) { continue; }
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException();
}
try {
args[i] = this.resolvers.resolveArgument(parameter,
mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
}
}
引数一つずつを処理
サポート可能な引数
か判定
サポート可能ならイ
ンスタンスを調達
【例】コントローラークラスの定義
(C) CASAREAL, Inc. All rights reserved. 93
@Controller
public class TestController {
@PostMapping("/test")
public String post(@Validated TestForm testForm,
BindingResult result,
Model model) {
return "test";
}
}
・Form
・BindingResult
・Model
の三つの引数を設定
動作概要
(C) CASAREAL, Inc. All rights reserved. 94
[0] [1] [2]
TestForm BindingResult Model
BindingResult担当
Form担当 Model担当
引数リスト
ArgumentResolverたち
引数リスト[0]
(C) CASAREAL, Inc. All rights reserved. 95
[0]
TestForm
BindingResult担当
Form担当 Model担当
引数リスト
俺の出番
だな!
サポート不可。。
引数リスト[1]
(C) CASAREAL, Inc. All rights reserved. 96
[1]
BindingResult
BindingResult担当
Form担当 Model担当
引数リスト
任せて!
サポート不可。。
引数リスト[2]
(C) CASAREAL, Inc. All rights reserved. 97
[2]
Model
BindingResult担当
Form担当 Model担当
引数リスト
いっきまーす!
出来ません!
引数に指定可能な型は?
(C) CASAREAL, Inc. All rights reserved. 98
 指定可能な型が多すぎる
https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-
framework-reference/web.html#mvc-ann-arguments
 Spring Security、Spring Data Commons等を追加すると
さらに増える
 丸暗記ではなく取捨選択が重要
 ソースコード上からも指定可能な型を確認できる
RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
(C) CASAREAL, Inc. All rights reserved. 99
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
(C) CASAREAL, Inc. All rights reserved. 100
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(),
this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
(C) CASAREAL, Inc. All rights reserved. 101
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
(C) CASAREAL, Inc. All rights reserved. 102
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
お腹いっぱいですね。( ;∀;)
(C) CASAREAL, Inc. All rights reserved. 103
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 104
 Form
 BindingResult
 Servlet API
 RequestBody
のArgumentResolverをご紹介します。
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 105
 Form
 BindingResult
 Servlet API
 RequestBody
ServletModelAttributeMethodProcessor
(C) CASAREAL, Inc. All rights reserved. 106
 根幹は親クラスのModelAttributeMethodProcessor
 Formクラスのインスタンスの生成と値の入力を行う
 @Validatedがついていたら入力検証も行う
 処理の最後にインスタンスのModelへの追加を行う
ModelAttributeMethodProcessor#supportsParameter()
(C) CASAREAL, Inc. All rights reserved. 107
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired &&
!BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
Number型やCharSequence型といった単純な型でない、
自作したFormクラスであればサポート対象
ModelAttributeMethodProcessor#resolveArgument()
(C) CASAREAL, Inc. All rights reserved. 108
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
throw ex;
}
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();
}
この処理が
インスタンス生成の始まり
ModelAttributeMethodProcessor#constructAttribute()
(C) CASAREAL, Inc. All rights reserved. 109
protected Object constructAttribute(Constructor<?> constructor,
String attributeName,
MethodParameter parameter,
WebDataBinderFactory binderFactory,
NativeWebRequest webRequest) throws Exception {
if (constructor.getParameterCount() == 0) {
// A single default constructor -> clearly a standard JavaBeans arrangement.
return BeanUtils.instantiateClass(constructor);
}
デフォルトコンストラクタの場合
はすぐにインスタンス化
引数ありコンストラクタの場合は
(C) CASAREAL, Inc. All rights reserved. 110
https://github.com/spring-projects/spring-framework/blob/master/spring-
web/src/main/java/org/springframework/web/method/annotation/ModelAttrib
uteMethodProcessor.java#L260
こちらをご参照ください
ModelAttributeMethodProcessor#resolveArgument()
(C) CASAREAL, Inc. All rights reserved. 111
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder,
parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
Form、BindingResult
のインスタンスを保持する
WebDataBinderを生成
フォームのsetterでパラメータの代入
Bean Validation実行
・入力検証でエラー
・引数にBindingResultがない
は例外スロー
ModelAttributeMethodProcessor#resolveArgument()
(C) CASAREAL, Inc. All rights reserved. 112
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
Form、BindingResultのインスタンスをModelにセット
この時、Form、BindingResultの順番で追加される
ModelAttributeMethodProcessor#isBindExceptionRequired ()
(C) CASAREAL, Inc. All rights reserved. 113
protected boolean isBindExceptionRequired(MethodParameter parameter) {
int i = parameter.getParameterIndex();
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1)
&& Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
コントローラーメソッドで、
Form、BindingResult
の順で引数を設定しないとエラーになるのは
ここでチェックしている
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 114
 Form
 BindingResult
 Servlet API
 RequestBody
ErrorsMethodArgumentResolver
(C) CASAREAL, Inc. All rights reserved. 115
 ServletModelAttributeMethodProcessorでModelに
追加されたBindingResultを返すだけ
 Modelの最後の要素を取得する
 ServletModelAttributeMethodProcessorで、
Form、BindingResultの順でModelに追加
 引数はForm、BindingResultの順で、
設定しないといけない
は、このためと考えられる
ErrorsMethodArgumentResolver#resolveArgument()
(C) CASAREAL, Inc. All rights reserved. 116
ModelMap model = mavContainer.getModel();
String lastKey = CollectionUtils.lastElement(model.keySet());
if (lastKey != null && lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return model.get(lastKey);
} Modelの最後の要素のkeyを取得して
valueを取得
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 117
 Form
 BindingResult
 Servlet API
 RequestBody
ServletRequestMethodArgumentResolver
ServletResponseMethodArgumentResolver
(C) CASAREAL, Inc. All rights reserved. 118
 ServletRequestMethodArgumentResolver
 ServletRequest、HttpSession、Principal等が該当する
 Spring Security の Authentication も該当
 ServletResponseMethodArgumentResolver
 ServletResponseなどが該当する
比較的単純な処理なのでソースコードの掲載は省略
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 119
 Form
 BindingResult
 Servlet API
 RequestBody
RequestResponseBodyMethodProcessor
(C) CASAREAL, Inc. All rights reserved. 120
 JSONをJavaオブジェクトに変換
 インスタンスの生成や値の代入はJacksonに委譲
 supportsParameterメソッドの判定条件は@RequestBody
がついているか否か
 @ValidatedがついていたらBean Validationを実行する
RequestResponseBodyMethodProcessor#resolveArgument()
(C) CASAREAL, Inc. All rights reserved. 121
public Object resolveArgument(
MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
Object arg = readWithMessageConverters(webRequest,
parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
Jacksonに JSON ⇒ Javaオブジェクト の変換処理を委譲
RequestResponseBodyMethodProcessor#resolveArgument() - 続き
(C) CASAREAL, Inc. All rights reserved. 122
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()
&& isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(..);
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name,
binder.getBindingResult());
}
}
Bean Validation を実行
引数にBindingResultが
ない場合は例外スロー
BindingResultだけ
Modelに追加
(C) CASAREAL, Inc. All rights reserved. 123
目次
(C) CASAREAL, Inc. All rights reserved. 124
1.Spring MVC のアーキテクチャー
2.URLとコントローラーメソッド
3.コントローラーメソッドの引数
4.コントローラーメソッドの戻り値
5.全体を俯瞰
Spring MVC のアーキテクチャー
(C) CASAREAL, Inc. All rights reserved. 125
Dispatcher
Servlet
Handler
Mapping
Handler
Adapter
View
Resolver Model
Controller
view name
View
1
2 3
4
56
Handler Adapter – 実行後
(C) CASAREAL, Inc. All rights reserved. 126
Dispatcher
Servlet
Handler
Adapter
Controller
view name
2 3
4
コントローラークラスのメソッドの
戻り値はどのように処理される?
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 127
 HandlerMethodReturnValueHandler
 HandlerAdapter(ここでも重要)
 ServletInvocableHandlerMethod (ここでも重要)
 ModelAndViewContainer
関係図
(C) CASAREAL, Inc. All rights reserved. 128
HandlerAdapter
HandlerMethodReturnValueHandler
ServletInvocableHandlerMethod
(リクエストごとに作られる)
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 129
 HandlerMethodReturnValueHandler
 HandlerAdapter(ここでも重要)
 ServletInvocableHandlerMethod (ここでも重要)
 ModelAndViewContainer
関係図
(C) CASAREAL, Inc. All rights reserved. 130
HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler
(C) CASAREAL, Inc. All rights reserved. 131
 コントローラーメソッドの戻り値を処理する
 戻り値の文字列をSpringにView名として認識させる
 RESTの場合は JSON への変換処理を呼び出す
 インタフェースであり、これを多数のクラスが実装
 String
 Map
 ResponseBody
など、各クラス分実装クラスが存在する
 Viewに処理を委譲するか、そのままレスポンスするか
のフラグの更新も担う
HandlerMethodReturnValueHandler
(C) CASAREAL, Inc. All rights reserved. 132
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception;
}
HandlerMethodReturnValueHandler
(C) CASAREAL, Inc. All rights reserved. 133
 supportsReturnType
 メソッドの戻り値の型や付加したアノテーションな
どからサポートするかを判定するメソッド
 handleReturnValue
 メソッドの戻り値を処理するメソッド
以後、ReturnValueHandler と省略した場合は、
こちらを示しています。
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 134
 HandlerMethodReturnValueHandler
 HandlerAdapter(ここでも重要)
 ServletInvocableHandlerMethod (ここでも重要)
 ModelAndViewContainer
関係図
(C) CASAREAL, Inc. All rights reserved. 135
HandlerAdapter
HandlerMethodReturnValueHandler
RequestMappingHandlerAdapter
(C) CASAREAL, Inc. All rights reserved. 136
 HandlerMethod用のHandlerAdapterの実装クラス
 HandlerMethodArgumentResolverのインスタンスを保持
 ServletInvocableHandlerMethodに
HandlerMethodArgumentResolverのインスタンスを渡し
て委譲する
 HandlerMethodReturnValueHandlerのインスタンスを保持
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 137
 HandlerMethodReturnValueHandler
 HandlerAdapter(ここでも重要)
 ServletInvocableHandlerMethod (ここでも重要)
 ModelAndViewContainer
関係図
(C) CASAREAL, Inc. All rights reserved. 138
HandlerAdapter
HandlerMethodReturnValueHandler
ServletInvocableHandlerMethod
(リクエストごとに作られる)
ServletInvocableHandlerMethod
(C) CASAREAL, Inc. All rights reserved. 139
 HandlerMethodを継承している
 RequestMappingHandlerAdapterから借り受けた
HandlerMethodArgumentResolverを使って引数のインス
タンスを調達する
 コントローラーメソッドを実行する
 HandlerMethodReturnValueHandlerを使って、戻り値の
処理を行う
関係図
(C) CASAREAL, Inc. All rights reserved. 140
RequestMapping
HandlerAdapter
ServletInvocable
HandlerMethod
this.returnValueHandlers
setHandlerMethodReturnValueHandlers
これも使って! 任せとけ!
リクエストごとに
new
押さえておきたい登場人物
(C) CASAREAL, Inc. All rights reserved. 141
 HandlerMethodReturnValueHandler
 HandlerAdapter(ここでも重要)
 ServletInvocableHandlerMethod (ここでも重要)
 ModelAndViewContainer
ModelAndViewContainer
(C) CASAREAL, Inc. All rights reserved. 142
 ModelとView名を保持するクラス
 DispatcherServletがViewを見つけ出す橋渡し役でもある
 Viewに処理を委譲する or そのままレスポンスする
を振り分けるフラグをもつ
 requestHandledフィールドが該当
ServletInvocableHandlerMethod#invokeAndHandle()
(C) CASAREAL, Inc. All rights reserved. 143
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest,
mavContainer, providedArgs);
// 中略
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
HandlerMethod実行後
の戻り値を受け取る
ServletInvocableHandlerMethod#invokeAndHandle()
(C) CASAREAL, Inc. All rights reserved. 144
try {
this.returnValueHandlers.handleReturnValue(
returnValue,
getReturnValueType(returnValue),
mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
戻り値の調整を
行う
【例】コントローラークラスの定義
(C) CASAREAL, Inc. All rights reserved. 145
@Controller
public class TestController {
@GetMapping("/")
public String index(Model model) {
model.addAttribute("name", "masato");
model.addAttribute("age", 1);
return "test";
}
@GetMapping("/test")
public Map test2() {
return Map.of("name", "masato", "age", 1);
}
}
戻り値の型が違うが
どちらも最終的に
“test.html” を出力
動作概要
(C) CASAREAL, Inc. All rights reserved. 146
Map担当
String担当 ResponseBody担当
戻り値の型
ReturnValueHandlerたち
?
(C) CASAREAL, Inc. All rights reserved. 147
Map担当
String担当 ResponseBody担当
戻り値の型
String
戻り値の型がString
俺の出番
だな!
サポート不可。。
(C) CASAREAL, Inc. All rights reserved. 148
Map担当
String担当 ResponseBody担当
戻り値の型
Map
戻り値の型がMap
任せとけ!
サポート不可。。
(C) CASAREAL, Inc. All rights reserved. 149
Map担当
String担当 ResponseBody担当
戻り値の型
ResponseBody
戻り値の型がResponseBody
任せとけ!
できない!
戻り値に指定可能な型は?
(C) CASAREAL, Inc. All rights reserved. 150
 指定可能な型が多すぎる
https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-
framework-reference/web.html#mvc-ann-return-types
 丸暗記ではなく取捨選択が重要
 ソースコード上からも指定可能な型を確認できる
RequestMappingHandlerAdapter#getDefaultReturnValueHandlers()
(C) CASAREAL, Inc. All rights reserved. 151
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor,
this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
(C) CASAREAL, Inc. All rights reserved. 152
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
(C) CASAREAL, Inc. All rights reserved. 153
// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(
getModelAndViewResolvers()));
}
else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}
いくつか絞ってご紹介します
(C) CASAREAL, Inc. All rights reserved. 154
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 155
 Viewに委譲するパターン
 String
 Map
 Viewに委譲しないパターン(REST)
 ResponseBody
 ResponseEntity
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 156
 Viewに委譲するパターン
 String
 Map
 Viewに委譲しないパターン(REST)
 ResponseBody
 ResponseEntity
ViewNameMethodReturnValueHandler
(C) CASAREAL, Inc. All rights reserved. 157
 戻り値を文字列にした場合が該当する
 戻り値をModelAndViewContainerが持つView名として
セットする
(C) CASAREAL, Inc. All rights reserved. 158
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType
|| CharSequence.class.isAssignableFrom(paramType));
}
ViewNameMethodReturnValueHandler#supportsReturnType()
意外にもサポート対象の型に
CharSequenceだけでなく
voidも含まれる
ViewNameMethodReturnValueHandler#handleReturnValue()
(C) CASAREAL, Inc. All rights reserved. 159
public void handleReturnValue(@Nullable Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
戻り値をViewName
としてセット
なぜvoidもサポート対象なのか?
(C) CASAREAL, Inc. All rights reserved. 160
 明示しない場合、リクエストURLが論理View名となる
 http://localhost:8080/test とアクセスした場合、
論理View名に “test” を指定した動作になる
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 161
 Viewに委譲するパターン
 String
 Map
 Viewに委譲しないパターン(REST)
 ResponseBody
 ResponseEntity
MapMethodProcessor
(C) CASAREAL, Inc. All rights reserved. 162
 戻り値をMap型にした場合が該当する
 戻り値をModelAndViewContainerの
Modelにすべて追加する
(C) CASAREAL, Inc. All rights reserved. 163
public boolean supportsReturnType(MethodParameter returnType) {
return Map.class.isAssignableFrom(returnType.getParameterType());
}
MapMethodProcessor#supportsReturnType()
戻り値がMap型がサポート対象
(C) CASAREAL, Inc. All rights reserved. 164
public void handleReturnValue(@Nullable Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof Map){
mavContainer.addAllAttributes((Map) returnValue);
}
}
MapMethodProcessor#handleReturnValue()
戻り値のMapの値をすべて
Modelに追加している
【例】コントローラークラスの定義
(C) CASAREAL, Inc. All rights reserved. 165
@Controller
public class TestController {
@GetMapping("/")
public String index(Model model) {
model.addAttribute("name", "masato");
model.addAttribute("age", 1);
return "test";
}
@GetMapping("/test")
public Map test2() {
return Map.of("name", "masato", "age", 1);
}
}
二つのメソッドは最終的に
同じ挙動になる
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 166
 Viewに委譲するパターン
 String
 Map
 Viewに委譲しないパターン(REST)
 ResponseBody
 ResponseEntity
RequestResponseBodyMethodProcessor
(C) CASAREAL, Inc. All rights reserved. 167
 @ResponseBodyが付加されている場合が該当する
 クラス、メソッドどちらにも付加可能
 @RestControllerを付加している場合も該当
 @Controllerと@ResponseBodyを合成したもの
 戻り値のJavaオブジェクトをJSONに変換して
レスポンス
(C) CASAREAL, Inc. All rights reserved. 168
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(
returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
RequestResponseBodyMethodProcessor#supportsReturnType()
メソッド、もしくはクラスに
@ResponseBodyが付加されてい
る場合サポート対象
(C) CASAREAL, Inc. All rights reserved. 169
public void handleReturnValue(
@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException,
HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
RequestResponseBodyMethodProcessor#handleReturnValue()
そのままレスポンスする設定
JSONに変換してレスポンスする処理
いくつか絞ってご紹介
(C) CASAREAL, Inc. All rights reserved. 170
 Viewに委譲するパターン
 String
 Map
 Viewに委譲しないパターン(REST)
 ResponseBody
 ResponseEntity
HttpEntityMethodProcessor
(C) CASAREAL, Inc. All rights reserved. 171
 戻り値をHttpEntity型もしくはResponseEntity型
にした場合が該当
 ResponseEntity型の場合レスポンスステータスをセット
 戻り値のbodyにセットしたJavaオブジェクトをJSONに
変換してレスポンス
(C) CASAREAL, Inc. All rights reserved. 172
public boolean supportsReturnType(MethodParameter returnType) {
return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) &&
!RequestEntity.class.isAssignableFrom(returnType.getParameterType()));
}
HttpEntityMethodProcessor#supportsReturnType()
HttpEntity型およびResponseEntity型
がサポート対象
(C) CASAREAL, Inc. All rights reserved. 173
public void handleReturnValue(
@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException,
HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
// 中略
if (responseEntity instanceof ResponseEntity) {
int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue();
outputMessage.getServletResponse().setStatus(returnStatus);
// JSONに変換する処理の呼び出し
}
HttpEntityMethodProcessor#handleReturnValue()
そのままレスポンスする設定
レスポンスステータスの設定
(C) CASAREAL, Inc. All rights reserved. 174
目次
(C) CASAREAL, Inc. All rights reserved. 175
1.Spring MVC のアーキテクチャー
2.URLとコントローラーメソッド
3.コントローラーメソッドの引数
4.コントローラーメソッドの戻り値
5.全体を俯瞰
Spring MVC のアーキテクチャー 再掲
(C) CASAREAL, Inc. All rights reserved. 176
Dispatcher
Servlet
Handler
Mapping
Handler
Adapter
View
Resolver Model
Controller
view name
View
1
2 3
4
56
DispatcherServlet#doDispatch()
が
Spring MVC の骨格です
(C) CASAREAL, Inc. All rights reserved. 177
Spring MVC のアーキテクチャー
(C) CASAREAL, Inc. All rights reserved. 178
Dispatcher
Servlet
Handler
Mapping
Controller
1
(C) CASAREAL, Inc. All rights reserved. 179
protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) throws Exception {
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
DispatcherServlet#doDispatch()
HandlerMapping呼び出し
(C) CASAREAL, Inc. All rights reserved. 180
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
DispatcherServlet#getHandler()
HandlerMapping呼び出し
Spring MVC のアーキテクチャ
(C) CASAREAL, Inc. All rights reserved. 181
Dispatcher
Servlet
Handler
Adapter
Controller
view name
2 3
4
(C) CASAREAL, Inc. All rights reserved. 182
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
DispatcherServlet#doDispatch()
HandlerAdapterの取り出し
コントローラーメソッドの実行
Spring MVC のアーキテクチャー
(C) CASAREAL, Inc. All rights reserved. 183
Dispatcher
Servlet
View
Resolver Model
View
56
(C) CASAREAL, Inc. All rights reserved. 184
processDispatchResult(processedRequest,
response, mappedHandler, mv, dispatchException);
DispatcherServlet#doDispatch()
Viewの解決とレンダリング処理
RESTの場合
(C) CASAREAL, Inc. All rights reserved. 185
(C) CASAREAL, Inc. All rights reserved. 186
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
// 中略
return mav;
}
requestHandledがtrueの場合は、
DispatcherServletに
ModelAndViewを返さない
RequestMappingHandlerAdapter#getModelAndView()
まとめ
(C) CASAREAL, Inc. All rights reserved. 187
 基本となるインタフェースと実装クラスを
抑えれば読解は可能
 引数のインスタンスの生成順序を正確に
把握できるのでトラブルに強くなれる
 initModelやinterceptorなど紹介していない
処理の呼び出しも是非読んでいただきたい
今後
(C) CASAREAL, Inc. All rights reserved. 188
 RequestMappingInfoの生成過程など、
より詳しく知るためにはDIコンテナの
生成過程を知る必要がある
 機会があればアプリの起動時の処理を紹介
したい
ご清聴ありがとうございました!
(C) CASAREAL, Inc. All rights reserved. 189

Weitere ähnliche Inhalte

Was ist angesagt?

とにかく分かりづらいTwelve-Factor Appの解説を試みる
とにかく分かりづらいTwelve-Factor Appの解説を試みるとにかく分かりづらいTwelve-Factor Appの解説を試みる
とにかく分かりづらいTwelve-Factor Appの解説を試みるMasatoshi Tada
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
さくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組みさくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組みTakeshi Ogawa
 
これで怖くない!?コードリーディングで学ぶSpring Security #中央線Meetup
これで怖くない!?コードリーディングで学ぶSpring Security #中央線Meetupこれで怖くない!?コードリーディングで学ぶSpring Security #中央線Meetup
これで怖くない!?コードリーディングで学ぶSpring Security #中央線MeetupMasatoshi Tada
 
Pivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_ccc
Pivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_cccPivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_ccc
Pivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_cccMasatoshi Tada
 
What's new in Spring Batch 5
What's new in Spring Batch 5What's new in Spring Batch 5
What's new in Spring Batch 5ikeyat
 
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -onozaty
 
ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説
ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説
ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説Masatoshi Tada
 
Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門Ryosuke Uchitate
 
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall ) LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall ) Hironobu Isoda
 
これからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきことこれからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきこと土岐 孝平
 
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...NTT DATA Technology & Innovation
 
Amazon EKS上の開発体験を最大化するプレビュー環境の作り方
Amazon EKS上の開発体験を最大化するプレビュー環境の作り方Amazon EKS上の開発体験を最大化するプレビュー環境の作り方
Amazon EKS上の開発体験を最大化するプレビュー環境の作り方理弘 山崎
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」Takuto Wada
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean ArchitectureAtsushi Nakamura
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 
Springを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイントSpringを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイント土岐 孝平
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)NTT DATA Technology & Innovation
 

Was ist angesagt? (20)

とにかく分かりづらいTwelve-Factor Appの解説を試みる
とにかく分かりづらいTwelve-Factor Appの解説を試みるとにかく分かりづらいTwelve-Factor Appの解説を試みる
とにかく分かりづらいTwelve-Factor Appの解説を試みる
 
Mavenの真実とウソ
Mavenの真実とウソMavenの真実とウソ
Mavenの真実とウソ
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
さくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組みさくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組み
 
これで怖くない!?コードリーディングで学ぶSpring Security #中央線Meetup
これで怖くない!?コードリーディングで学ぶSpring Security #中央線Meetupこれで怖くない!?コードリーディングで学ぶSpring Security #中央線Meetup
これで怖くない!?コードリーディングで学ぶSpring Security #中央線Meetup
 
Pivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_ccc
Pivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_cccPivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_ccc
Pivotal認定講師によるSpring Framework 5.1 & Spring Boot 2.1ハンズオン! #jjug_ccc
 
What's new in Spring Batch 5
What's new in Spring Batch 5What's new in Spring Batch 5
What's new in Spring Batch 5
 
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
 
ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説
ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説
ReactiveだけじゃないSpring 5 & Spring Boot 2新機能解説
 
Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門
 
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall ) LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
 
これからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきことこれからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきこと
 
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
 
Amazon EKS上の開発体験を最大化するプレビュー環境の作り方
Amazon EKS上の開発体験を最大化するプレビュー環境の作り方Amazon EKS上の開発体験を最大化するプレビュー環境の作り方
Amazon EKS上の開発体験を最大化するプレビュー環境の作り方
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
Springを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイントSpringを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイント
 
At least onceってぶっちゃけ問題の先送りだったよね #kafkajp
At least onceってぶっちゃけ問題の先送りだったよね #kafkajpAt least onceってぶっちゃけ問題の先送りだったよね #kafkajp
At least onceってぶっちゃけ問題の先送りだったよね #kafkajp
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
 

Ähnlich wie 【Spring fest 2019】徹底解剖Spring MVCアーキテクチャー

RETEアルゴリズムを使いこなせ
RETEアルゴリズムを使いこなせRETEアルゴリズムを使いこなせ
RETEアルゴリズムを使いこなせMasahiko Umeno
 
第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)
第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)
第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)Masahiro Kanazaki
 
db-tech-showcase-sapporo-b24-20150911p
db-tech-showcase-sapporo-b24-20150911pdb-tech-showcase-sapporo-b24-20150911p
db-tech-showcase-sapporo-b24-20150911pSatoru Ishikawa
 
タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-
タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-
タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-Takakiyo Tanaka
 
Rails3.2ってどう変わるの?
Rails3.2ってどう変わるの?Rails3.2ってどう変わるの?
Rails3.2ってどう変わるの?Takafumi ONAKA
 
サービス開発における フロントエンド・ドメイン駆動設計の実践
サービス開発における フロントエンド・ドメイン駆動設計の実践サービス開発における フロントエンド・ドメイン駆動設計の実践
サービス開発における フロントエンド・ドメイン駆動設計の実践TakefumiYoshii
 
C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml ssuser3a4b8c
 
Redshift Spectrumを使ってみた話
Redshift Spectrumを使ってみた話Redshift Spectrumを使ってみた話
Redshift Spectrumを使ってみた話Yoshiki Kouno
 

Ähnlich wie 【Spring fest 2019】徹底解剖Spring MVCアーキテクチャー (11)

RETEアルゴリズムを使いこなせ
RETEアルゴリズムを使いこなせRETEアルゴリズムを使いこなせ
RETEアルゴリズムを使いこなせ
 
第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)
第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)
第21回新生流体科学セミナー@宇宙航空研究開発機構(JAXA)
 
Java class design
Java class designJava class design
Java class design
 
db-tech-showcase-sapporo-b24-20150911p
db-tech-showcase-sapporo-b24-20150911pdb-tech-showcase-sapporo-b24-20150911p
db-tech-showcase-sapporo-b24-20150911p
 
C++0x concept
C++0x conceptC++0x concept
C++0x concept
 
Tokyo r #43
Tokyo r #43Tokyo r #43
Tokyo r #43
 
タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-
タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-
タイムマシン採用:明日のエンタープライズJavaの世界を予想する -Java EE7/クラウド/Docker/etc.-
 
Rails3.2ってどう変わるの?
Rails3.2ってどう変わるの?Rails3.2ってどう変わるの?
Rails3.2ってどう変わるの?
 
サービス開発における フロントエンド・ドメイン駆動設計の実践
サービス開発における フロントエンド・ドメイン駆動設計の実践サービス開発における フロントエンド・ドメイン駆動設計の実践
サービス開発における フロントエンド・ドメイン駆動設計の実践
 
C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml C base design methodology with s dx and xilinx ml
C base design methodology with s dx and xilinx ml
 
Redshift Spectrumを使ってみた話
Redshift Spectrumを使ってみた話Redshift Spectrumを使ってみた話
Redshift Spectrumを使ってみた話
 

【Spring fest 2019】徹底解剖Spring MVCアーキテクチャー

  • 2. 突然ですが (C) CASAREAL, Inc. All rights reserved. 2 @Controller public class TestController { @PostMapping("/test") public String post(@Validated TestForm testForm, BindingResult result, Model model) { return "test"; } } ごくごく一般的なコントローラー の実装ですよね!?
  • 3. 気になったことはありませんか? (C) CASAREAL, Inc. All rights reserved. 3 @Controller public class TestController { @PostMapping("/test") public String post(@Validated TestForm testForm, BindingResult result, Model model) { return "test"; } } リクエストURLと メソッドの関係は? 引数のインスタンス はどこからくる? 戻り値って文字列以外でもOK?
  • 4. 気になったことはありませんか? (C) CASAREAL, Inc. All rights reserved. 4 @Controller public class TestController { @PostMapping("/test") public String post(@Validated TestForm testForm, BindingResult result, Model model) { return "test"; } } 引数に設定可能な 型って何個ある? @ValidatedとBindingResult以外って 順番決まりなさそうだけど。。。
  • 6. このセッションは (C) CASAREAL, Inc. All rights reserved. 6 Spring MVCのアーキテクチャーを ソースコードリーディングをもとに解き明か していく45分間です Spring MVCのアーキテクチャーをより詳しく 知りたい方が対象です 内部構造を詳細に把握することでトラブルに 強くなれます
  • 7. 自己紹介:菊池真登 (C) CASAREAL, Inc. All rights reserved. 7  研修トレーナー@カサレアル  登壇実績:Spring / Java SE  開発歴[Spring]:2012年から  開発歴[Angular]:2015年頃に 1.5年間AngularJSに触れて冬眠
  • 8. 株式会社カサレアル (C) CASAREAL, Inc. All rights reserved. 8 他社にはない色々なプログラミング言語の 研修を提供しています!
  • 9. 目次 (C) CASAREAL, Inc. All rights reserved. 9 1.Spring MVC のアーキテクチャー 2.URLとコントローラーメソッド 3.コントローラーメソッドの引数 4.コントローラーメソッドの戻り値 5.全体を俯瞰
  • 10. 目次 (C) CASAREAL, Inc. All rights reserved. 10 1.Spring MVC のアーキテクチャー 2.URLとコントローラーメソッド 3.コントローラーメソッドの引数 4.コントローラーメソッドの戻り値 5.全体を俯瞰
  • 11. Spring MVC のアーキテクチャー (C) CASAREAL, Inc. All rights reserved. 11 Dispatcher Servlet Handler Mapping Handler Adapter View Resolver Model Controller view name View 1 2 3 4 56
  • 12. 関係を詳しく見ると (C) CASAREAL, Inc. All rights reserved. 12 Dispatcher Servlet DIコンテナ Handler Mapping Handler Adapter DispatcherServletはDIコンテナをもつ そのDIコンテナがHandlerMappingや HandlerAdapterなどをもつ
  • 13. 処理実行時は (C) CASAREAL, Inc. All rights reserved. 13 Dispatcher Servlet Handler Adapter Handler Mapping DispatcherServletの初期化処理で DIコンテナからインスタンスを取り出し フィールドに直接保持する
  • 14. DispatcherServlet#initHandlerMappings() - 一部 (C) CASAREAL, Inc. All rights reserved. 14 private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerMappings); } } インスタンスを取り出し、 フィールド変数に代入
  • 15. HandlerMapping (C) CASAREAL, Inc. All rights reserved. 15 Dispatcher Servlet Handler Mapping Controller 1 リクエストURLとコントローラー クラスのメソッドとの関係とは?
  • 16. HandlerAdapter – 実行前 (C) CASAREAL, Inc. All rights reserved. 16 Dispatcher Servlet Handler Adapter Controller 2 3 コントローラークラスのメソッド の引数はどのようにして解決?
  • 17. HandlerAdapter – 実行後 (C) CASAREAL, Inc. All rights reserved. 17 Dispatcher Servlet Handler Adapter Controller view name 2 3 4 コントローラークラスのメソッドの 戻り値はどのように処理される?
  • 18. 目次 (C) CASAREAL, Inc. All rights reserved. 18 1.Spring MVC のアーキテクチャー 2.URLとコントローラーメソッド 3.コントローラーメソッドの引数 4.コントローラーメソッドの戻り値 5.全体を俯瞰
  • 19. Spring MVC のアーキテクチャー (C) CASAREAL, Inc. All rights reserved. 19 Dispatcher Servlet Handler Mapping Handler Adapter View Resolver Model Controller view name View 1 2 3 4 56
  • 20. HandlerMapping (C) CASAREAL, Inc. All rights reserved. 20 Dispatcher Servlet Handler Mapping Controller 1 リクエストURLとコントローラー クラスのメソッドとの関係とは?
  • 21. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 21  HandlerMapping  RequestMappingInfo  MappingRegistry  HandlerMethod  AbstractHandlerMethodMapping
  • 22. ざっくりとした関係図 (C) CASAREAL, Inc. All rights reserved. 22 HandlerMapping MappingRegistry RequestMappingInfo RequestMappingInfo RequestMappingInfo HandlerMethod HandlerMethod HandlerMethod
  • 23. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 23  HandlerMapping  RequestMappingInfo  MappingRegistry  HandlerMethod  AbstractHandlerMethodMapping
  • 24. 関係図 (C) CASAREAL, Inc. All rights reserved. 24 HandlerMapping
  • 25. HandlerMapping (C) CASAREAL, Inc. All rights reserved. 25  インタフェース  getHandlerメソッドが一つだけ定義されている  戻り値が現在のリクエストを処理するメソッドになる HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
  • 26. RequestMappingHandlerMapping (C) CASAREAL, Inc. All rights reserved. 26  HandlerMappingを実装したクラス  コントローラークラス用  @EnableWebMvcアノテーションで有効化  Spring Bootではアノテーションは不要  一般的には下記の説明 Bean定義されているContorllerから @RequestMappingアノテーションを読み取り、 URLと合致するControllerのメソッドを探す
  • 27. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 27  HandlerMapping  RequestMappingInfo  MappingRegistry  HandlerMethod  AbstractHandlerMethodMapping
  • 28. HandlerMapping 関係図 (C) CASAREAL, Inc. All rights reserved. 28 RequestMappingInfo RequestMappingInfo RequestMappingInfo
  • 29. RequestMappingInfo (C) CASAREAL, Inc. All rights reserved. 29  @RequestMappingの設定値を保持する  コントローラーメソッドの定義情報を表すクラス  コントローラーメソッドの数だけ作られる  1メソッド : 1Info  アプリ実行中はMappingRegistryに保持される
  • 30. RequestMappingInfo (C) CASAREAL, Inc. All rights reserved. 30 public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> { @Nullable private final String name; private final PatternsRequestCondition patternsCondition; private final RequestMethodsRequestCondition methodsCondition; @RequestMappingの value:URL method:リクエストメソッド の設定値を保持する
  • 31. RequestMappingInfo 続き (C) CASAREAL, Inc. All rights reserved. 31 private final ParamsRequestCondition paramsCondition; private final HeadersRequestCondition headersCondition; private final ConsumesRequestCondition consumesCondition; private final ProducesRequestCondition producesCondition; private final RequestConditionHolder customConditionHolder; params headers consumes produces の設定値を 保持する
  • 32. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 32  HandlerMapping  RequestMappingInfo  MappingRegistry  HandlerMethod  AbstractHandlerMethodMapping
  • 33. 関係図 (C) CASAREAL, Inc. All rights reserved. 33 HandlerMapping MappingRegistry RequestMappingInfo RequestMappingInfo RequestMappingInfo
  • 34. MappingRegistry (C) CASAREAL, Inc. All rights reserved. 34  リクエストURLとRequestMappingInfoを紐づける  AbstractHandlerMethodMapping内にあるクラス  アクセス修飾子なしのクラス  実行中はAbstractHandlerMethodMappingのフィールドに インスタンスが保持される
  • 35. MappingRegistry (C) CASAREAL, Inc. All rights reserved. 35 class MappingRegistry { private final Map<RequestMappingInfo,HandlerMethod> mappingLookup; private final MultiValueMap<String, RequestMappingInfo > urlLookup; @RequestMappingアノテーションのvalue属性すなわち URL を key にして、 RequestMappingInfo が保持される RequestMappingInfo を key に HandlerMethod が保持される *見やすさのため、実際のソースコードとは異なります
  • 36. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 36  HandlerMapping  RequestMappingInfo  MappingRegistry  HandlerMethod  AbstractHandlerMethodMapping
  • 37. 関係図 (C) CASAREAL, Inc. All rights reserved. 37 HandlerMapping MappingRegistry RequestMappingInfo RequestMappingInfo RequestMappingInfo HandlerMethod HandlerMethod HandlerMethod
  • 38. HandlerMethod (C) CASAREAL, Inc. All rights reserved. 38  コントローラーメソッドの化身  メソッドのリフレクションをもつ  フレームワーク内部でコントローラメソッドを 取り扱うためのもの
  • 40. 【例】コントローラークラスの定義 (C) CASAREAL, Inc. All rights reserved. 40 @Controller public class TestController { @GetMapping("/") public String index() { return "index"; } @GetMapping("/test") public String get() { return "get"; } @PostMapping("/test") public String post() { return "post"; } } ・URL:”/”、メソッド: GET ・URL:”/test”、メソッド GET ・URL:”/test”、メソッド POST の3つを定義する
  • 41. MappingRegistryのurlLookupの状態 (C) CASAREAL, Inc. All rights reserved. 41 “/” “/test” Patterns:”/” Methods:”GET” Patterns:”/test” Methods:”GET” Patterns:”/test” Methods:”POST” key : URL value : RequestMappingInfo
  • 42. MappingRegistryのmappingLookupの状態 (C) CASAREAL, Inc. All rights reserved. 42 Patterns:”/” Methods:”GET” Patterns:”/test” Methods:”GET” Patterns:”/test” Methods:”POST” key : RequestMappingInfo value : HandlerMethod TestController#index() TestController#get() TestController#post()
  • 43. まとめると (C) CASAREAL, Inc. All rights reserved. 43 Patterns:”/” Methods:”GET” Patterns:”/test” Methods:”GET” Patterns:”/test” Methods:”POST” TestController#index() TestController#get() TestController#post() “/” “/test” URL RequestMappingInfo HandlerMethod
  • 44. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 44  HandlerMapping  RequestMappingInfo  MappingRegistry  HandlerMethod  AbstractHandlerMethodMapping
  • 45. 継承関係 (C) CASAREAL, Inc. All rights reserved. 45 RequestMappingHandlerMapping AbstractHandlerMethodMapping HandlerMapping
  • 46. AbstractHandlerMethodMapping (C) CASAREAL, Inc. All rights reserved. 46  HandlerMappingの処理の根幹  RequestMappingHandlerMappingが継承  lookupHandlerMethodメソッドが肝! ざっくりいうと下記の二段構成 1. リクエストURLと完全一致するものを探す 2. なければ全検索でパターンマッチを行う
  • 48. AbstractHandlerMethodMapping#lookupHandlerMethod() (C) CASAREAL, Inc. All rights reserved. 48 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } リクエストURLをKeyに RequestMappingInfoを取り出す
  • 49. MappingRegistry#getMappingsByUrl() (C) CASAREAL, Inc. All rights reserved. 49 class MappingRegistry { private final MultiValueMap<String, RequestMappingInfo> urlLookup public List<RequestMappingInfo> getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); }
  • 50. 【例】リクエストURLが / の場合 (C) CASAREAL, Inc. All rights reserved. 50 “/” Patterns:”/” Methods:”GET” このRequestMappingInfo が選択される key : URL value : RequestMappingInfo
  • 51. 【例】リクエストURLが /test の場合 (C) CASAREAL, Inc. All rights reserved. 51 “/test” Patterns:”/test” Methods:”GET” Patterns:”/test” Methods:”POST” これらのRequestMappingInfo が選択される key : URL value : RequestMappingInfo
  • 52. リクエストメソッドなど細かい判定を行う (C) CASAREAL, Inc. All rights reserved. 52  リクエストメソッド等細かな条件で絞り込む  RequestMappingInfo#getMatchingCondition()で行う  @RequestMappingのすべての属性値が判定対象  method  params  headers  consumes  produces
  • 53. AbstractHandlerMethodMapping#lookupHandlerMethod() (C) CASAREAL, Inc. All rights reserved. 53 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<RequestMappingInfo> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } リクエストメソッドなどの より細かな精査を行う
  • 54. RequestMappingInfo#getMatchingCondition() (C) CASAREAL, Inc. All rights reserved. 54 public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); if (methods == null) { // リクエストメソッドの判定 return null; } ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); if (params == null) { // リクエストパラメータの判定 return null; } 先ほどのaddMatchingMappings メソッドの呼び出しで 最終的に呼ばれるメソッド
  • 55. getMatchingCondition - 続き (C) CASAREAL, Inc. All rights reserved. 55 HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); if (headers == null) { // リクエストヘッダーの判定 return null; } ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); if (consumes == null) { // Content-Typeの判定 return null; }
  • 56. getMatchingCondition - 続き (C) CASAREAL, Inc. All rights reserved. 56 ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); if (produces == null) { // Acceptの判定 return null; } PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); if (patterns == null) { // リクエストURLのパターン判定 return null; }
  • 57. getMatchingCondition - 続き (C) CASAREAL, Inc. All rights reserved. 57 RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); if (custom == null) { // カスタム判定 return null; } return new RequestMappingInfo(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
  • 58. (C) CASAREAL, Inc. All rights reserved. 58
  • 60. (C) CASAREAL, Inc. All rights reserved. 60 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { addMatchingMappings( this.mappingRegistry.getMappings().keySet(), matches, request); } 全RequestMappingInfoから パターンマッチを行う AbstractHandlerMethodMapping#lookupHandlerMethod()
  • 61. 【例】コントローラークラスの定義 (C) CASAREAL, Inc. All rights reserved. 61 @Controller public class TestController { @GetMapping("/**") public String index() { return "index"; } @GetMapping("/test") public String get() { return "get"; } } ・URL:”/**”、メソッド: GET ・URL:”/test”、メソッド GET の2つを定義する
  • 62. MappingRegistryのurlLookupの状態 (C) CASAREAL, Inc. All rights reserved. 62 “/**” “/test” Patterns:”/**” Methods:”GET” Patterns:”/test” Methods:”GET” key : URL value : RequestMappingInfo
  • 63. MappingRegistryのmappingLookupの状態 (C) CASAREAL, Inc. All rights reserved. 63 Patterns:”/**” Methods:”GET” Patterns:”/test” Methods:”GET” TestController#index() TestController#get() key : RequestMappingInfo value : HandlerMethod
  • 64. リクエストURL : “/” メソッド : “GET” (C) CASAREAL, Inc. All rights reserved. 64 Patterns:”/**” Methods:”GET” Patterns:”/test” Methods:”GET” TestController#index() TestController#get() “/**” “/test” リクエストURL : / は完全一致しない 全RequestMappingInfo からパターンマッチ を行う
  • 66. RequestMappingInfo#getMatchingCondition() – 再掲 (C) CASAREAL, Inc. All rights reserved. 66 public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); if (methods == null) { return null; } // 中略 PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); if (patterns == null) { return null; } @GetMapping("/**") メソッド : GET、にマッチ @GetMapping("/**") URL : /**、にマッチ
  • 67. 【例】コントローラークラスの定義 (C) CASAREAL, Inc. All rights reserved. 67 @Controller public class TestController { @GetMapping("/**") public String index() { return "index"; } @GetMapping("/test") public String get() { return "get"; } } ゆえに、indexメソッドが選択 される
  • 68. (C) CASAREAL, Inc. All rights reserved. 68
  • 70. 目次 (C) CASAREAL, Inc. All rights reserved. 70 1.Spring MVC のアーキテクチャー 2.URLとコントローラーメソッド 3.コントローラーメソッドの引数 4.コントローラーメソッドの戻り値 5.全体を俯瞰
  • 71. Spring MVC のアーキテクチャー (C) CASAREAL, Inc. All rights reserved. 71 Dispatcher Servlet Handler Mapping Handler Adapter View Resolver Model Controller view name View 1 2 3 4 56
  • 72. Handler Adapter – 実行前 (C) CASAREAL, Inc. All rights reserved. 72 Dispatcher Servlet Handler Adapter Controller 2 3 コントローラークラスのメソッド の引数はどのようにして解決?
  • 73. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 73  HandlerMethodArgumentResolver  HandlerAdapter  ServletInvocableHandlerMethod
  • 74. 関係図 (C) CASAREAL, Inc. All rights reserved. 74 HandlerAdapter HandlerMethodArgumentResolver ServletInvocableHandlerMethod (リクエストごとに作られる)
  • 75. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 75  HandlerMethodArgumentResolver  HandlerAdapter  ServletInvocableHandlerMethod
  • 76. 関係図 (C) CASAREAL, Inc. All rights reserved. 76 HandlerMethodArgumentResolver
  • 77. HandlerMethodArgumentResolver (C) CASAREAL, Inc. All rights reserved. 77  コントローラーメソッドの引数の調達役  インタフェースであり、これを多数のクラスが実装  Form  BindingResult  Servlet API など、各クラス分実装クラスが存在する  Spring SecurityやSpring Data Commons 等にも実装クラス が存在する  メソッドで UserDetails 等を取得するのもこの機能
  • 78. HandlerMethodArgumentResolver (C) CASAREAL, Inc. All rights reserved. 78 public interface HandlerMethodArgumentResolver { boolean supportsParameter(MethodParameter parameter); Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; }
  • 79. HandlerMethodArgumentResolver (C) CASAREAL, Inc. All rights reserved. 79  supportsParameter()  メソッド引数の型や付加したアノテーションなどか らサポート対象かを判定するメソッド  resolveArgument()  メソッド引数のインスタンスを調達するメソッド 以後、ArgumentResolver と省略した場合は、 こちらを示しています。
  • 80. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 80  HandlerMethodArgumentResolver  HandlerAdapter  ServletInvocableHandlerMethod
  • 81. 関係図 (C) CASAREAL, Inc. All rights reserved. 81 HandlerAdapter HandlerMethodArgumentResolver
  • 82. HandlerAdapter (C) CASAREAL, Inc. All rights reserved. 82  HandlerMappingで選択されたHandlerMethodを実行する  インタフェース  handle()メソッド内で HandlerMethod を実行する
  • 83. RequestMappingHandlerAdapter (C) CASAREAL, Inc. All rights reserved. 83  @RequestMappingで定義されたHandlerMethodを サポートするHandlerAdapterの実装クラス  HandlerMethodArgumentResolverのインスタンスを保持  ServletInvocableHandlerMethodに HandlerMethodArgumentResolverのインスタンスを 渡して委譲する
  • 84. 継承関係 (C) CASAREAL, Inc. All rights reserved. 84 RequestMappingHandlerAdapter HandlerAdapter
  • 85. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 85  HandlerMethodArgumentResolver  HandlerAdapter  ServletInvocableHandlerMethod
  • 86. 関係図 (C) CASAREAL, Inc. All rights reserved. 86 HandlerAdapter HandlerMethodArgumentResolver ServletInvocableHandlerMethod (リクエストごとに作られる)
  • 87. ServletInvocableHandlerMethod (C) CASAREAL, Inc. All rights reserved. 87  HandlerMethodを継承している  RequestMappingHandlerAdapterから借り受けた HandlerMethodArgumentResolverを使って 引数のインスタンスを調達する  コントローラーメソッドを実行する
  • 88. 継承関係 (C) CASAREAL, Inc. All rights reserved. 88 ServletInvocableHandlerMethod HandlerMethod
  • 89. 関係図 (C) CASAREAL, Inc. All rights reserved. 89 RequestMapping HandlerAdapter ServletInvocable HandlerMethod リクエストごとに new this.argumentResolvers setHandlerMethodArgumentResolvers これ使って! 任せとけ!
  • 90. InvocableHandlerMethod#invokeForRequest() (C) CASAREAL, Inc. All rights reserved. 90 public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } return doInvoke(args); } 引数のインスタンス を調達 メソッドの実行!
  • 91. InvocableHandlerMethod#getMethodArgumentValues () (C) CASAREAL, Inc. All rights reserved. 91 protected Object[] getMethodArgumentValues( NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } 引数なしの場合は そのままreturn 引数リストを取得
  • 92. InvocableHandlerMethod#getMethodArgumentValues() (C) CASAREAL, Inc. All rights reserved. 92 Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(); } try { args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { } } 引数一つずつを処理 サポート可能な引数 か判定 サポート可能ならイ ンスタンスを調達
  • 93. 【例】コントローラークラスの定義 (C) CASAREAL, Inc. All rights reserved. 93 @Controller public class TestController { @PostMapping("/test") public String post(@Validated TestForm testForm, BindingResult result, Model model) { return "test"; } } ・Form ・BindingResult ・Model の三つの引数を設定
  • 94. 動作概要 (C) CASAREAL, Inc. All rights reserved. 94 [0] [1] [2] TestForm BindingResult Model BindingResult担当 Form担当 Model担当 引数リスト ArgumentResolverたち
  • 95. 引数リスト[0] (C) CASAREAL, Inc. All rights reserved. 95 [0] TestForm BindingResult担当 Form担当 Model担当 引数リスト 俺の出番 だな! サポート不可。。
  • 96. 引数リスト[1] (C) CASAREAL, Inc. All rights reserved. 96 [1] BindingResult BindingResult担当 Form担当 Model担当 引数リスト 任せて! サポート不可。。
  • 97. 引数リスト[2] (C) CASAREAL, Inc. All rights reserved. 97 [2] Model BindingResult担当 Form担当 Model担当 引数リスト いっきまーす! 出来ません!
  • 98. 引数に指定可能な型は? (C) CASAREAL, Inc. All rights reserved. 98  指定可能な型が多すぎる https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring- framework-reference/web.html#mvc-ann-arguments  Spring Security、Spring Data Commons等を追加すると さらに増える  丸暗記ではなく取捨選択が重要  ソースコード上からも指定可能な型を確認できる
  • 99. RequestMappingHandlerAdapter#getDefaultArgumentResolvers() (C) CASAREAL, Inc. All rights reserved. 99 private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false));
  • 100. RequestMappingHandlerAdapter#getDefaultArgumentResolvers() (C) CASAREAL, Inc. All rights reserved. 100 resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver());
  • 101. RequestMappingHandlerAdapter#getDefaultArgumentResolvers() (C) CASAREAL, Inc. All rights reserved. 101 // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
  • 102. RequestMappingHandlerAdapter#getDefaultArgumentResolvers() (C) CASAREAL, Inc. All rights reserved. 102 // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
  • 104. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 104  Form  BindingResult  Servlet API  RequestBody のArgumentResolverをご紹介します。
  • 105. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 105  Form  BindingResult  Servlet API  RequestBody
  • 106. ServletModelAttributeMethodProcessor (C) CASAREAL, Inc. All rights reserved. 106  根幹は親クラスのModelAttributeMethodProcessor  Formクラスのインスタンスの生成と値の入力を行う  @Validatedがついていたら入力検証も行う  処理の最後にインスタンスのModelへの追加を行う
  • 107. ModelAttributeMethodProcessor#supportsParameter() (C) CASAREAL, Inc. All rights reserved. 107 public boolean supportsParameter(MethodParameter parameter) { return (parameter.hasParameterAnnotation(ModelAttribute.class) || (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType()))); } Number型やCharSequence型といった単純な型でない、 自作したFormクラスであればサポート対象
  • 108. ModelAttributeMethodProcessor#resolveArgument() (C) CASAREAL, Inc. All rights reserved. 108 try { attribute = createAttribute(name, parameter, binderFactory, webRequest); } catch (BindException ex) { if (isBindExceptionRequired(parameter)) { throw ex; } if (parameter.getParameterType() == Optional.class) { attribute = Optional.empty(); } bindingResult = ex.getBindingResult(); } この処理が インスタンス生成の始まり
  • 109. ModelAttributeMethodProcessor#constructAttribute() (C) CASAREAL, Inc. All rights reserved. 109 protected Object constructAttribute(Constructor<?> constructor, String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception { if (constructor.getParameterCount() == 0) { // A single default constructor -> clearly a standard JavaBeans arrangement. return BeanUtils.instantiateClass(constructor); } デフォルトコンストラクタの場合 はすぐにインスタンス化
  • 110. 引数ありコンストラクタの場合は (C) CASAREAL, Inc. All rights reserved. 110 https://github.com/spring-projects/spring-framework/blob/master/spring- web/src/main/java/org/springframework/web/method/annotation/ModelAttrib uteMethodProcessor.java#L260 こちらをご参照ください
  • 111. ModelAttributeMethodProcessor#resolveArgument() (C) CASAREAL, Inc. All rights reserved. 111 if (bindingResult == null) { // Bean property binding and validation; // skipped in case of binding failure on construction. WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest); } validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } } Form、BindingResult のインスタンスを保持する WebDataBinderを生成 フォームのsetterでパラメータの代入 Bean Validation実行 ・入力検証でエラー ・引数にBindingResultがない は例外スロー
  • 112. ModelAttributeMethodProcessor#resolveArgument() (C) CASAREAL, Inc. All rights reserved. 112 // Add resolved attribute and BindingResult at the end of the model Map<String, Object> bindingResultModel = bindingResult.getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return attribute; Form、BindingResultのインスタンスをModelにセット この時、Form、BindingResultの順番で追加される
  • 113. ModelAttributeMethodProcessor#isBindExceptionRequired () (C) CASAREAL, Inc. All rights reserved. 113 protected boolean isBindExceptionRequired(MethodParameter parameter) { int i = parameter.getParameterIndex(); Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes(); boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1])); return !hasBindingResult; } コントローラーメソッドで、 Form、BindingResult の順で引数を設定しないとエラーになるのは ここでチェックしている
  • 114. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 114  Form  BindingResult  Servlet API  RequestBody
  • 115. ErrorsMethodArgumentResolver (C) CASAREAL, Inc. All rights reserved. 115  ServletModelAttributeMethodProcessorでModelに 追加されたBindingResultを返すだけ  Modelの最後の要素を取得する  ServletModelAttributeMethodProcessorで、 Form、BindingResultの順でModelに追加  引数はForm、BindingResultの順で、 設定しないといけない は、このためと考えられる
  • 116. ErrorsMethodArgumentResolver#resolveArgument() (C) CASAREAL, Inc. All rights reserved. 116 ModelMap model = mavContainer.getModel(); String lastKey = CollectionUtils.lastElement(model.keySet()); if (lastKey != null && lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) { return model.get(lastKey); } Modelの最後の要素のkeyを取得して valueを取得
  • 117. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 117  Form  BindingResult  Servlet API  RequestBody
  • 118. ServletRequestMethodArgumentResolver ServletResponseMethodArgumentResolver (C) CASAREAL, Inc. All rights reserved. 118  ServletRequestMethodArgumentResolver  ServletRequest、HttpSession、Principal等が該当する  Spring Security の Authentication も該当  ServletResponseMethodArgumentResolver  ServletResponseなどが該当する 比較的単純な処理なのでソースコードの掲載は省略
  • 119. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 119  Form  BindingResult  Servlet API  RequestBody
  • 120. RequestResponseBodyMethodProcessor (C) CASAREAL, Inc. All rights reserved. 120  JSONをJavaオブジェクトに変換  インスタンスの生成や値の代入はJacksonに委譲  supportsParameterメソッドの判定条件は@RequestBody がついているか否か  @ValidatedがついていたらBean Validationを実行する
  • 121. RequestResponseBodyMethodProcessor#resolveArgument() (C) CASAREAL, Inc. All rights reserved. 121 public Object resolveArgument( MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { parameter = parameter.nestedIfOptional(); Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); String name = Conventions.getVariableNameForParameter(parameter); Jacksonに JSON ⇒ Javaオブジェクト の変換処理を委譲
  • 122. RequestResponseBodyMethodProcessor#resolveArgument() - 続き (C) CASAREAL, Inc. All rights reserved. 122 if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); if (arg != null) { validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(..); } } if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } Bean Validation を実行 引数にBindingResultが ない場合は例外スロー BindingResultだけ Modelに追加
  • 123. (C) CASAREAL, Inc. All rights reserved. 123
  • 124. 目次 (C) CASAREAL, Inc. All rights reserved. 124 1.Spring MVC のアーキテクチャー 2.URLとコントローラーメソッド 3.コントローラーメソッドの引数 4.コントローラーメソッドの戻り値 5.全体を俯瞰
  • 125. Spring MVC のアーキテクチャー (C) CASAREAL, Inc. All rights reserved. 125 Dispatcher Servlet Handler Mapping Handler Adapter View Resolver Model Controller view name View 1 2 3 4 56
  • 126. Handler Adapter – 実行後 (C) CASAREAL, Inc. All rights reserved. 126 Dispatcher Servlet Handler Adapter Controller view name 2 3 4 コントローラークラスのメソッドの 戻り値はどのように処理される?
  • 127. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 127  HandlerMethodReturnValueHandler  HandlerAdapter(ここでも重要)  ServletInvocableHandlerMethod (ここでも重要)  ModelAndViewContainer
  • 128. 関係図 (C) CASAREAL, Inc. All rights reserved. 128 HandlerAdapter HandlerMethodReturnValueHandler ServletInvocableHandlerMethod (リクエストごとに作られる)
  • 129. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 129  HandlerMethodReturnValueHandler  HandlerAdapter(ここでも重要)  ServletInvocableHandlerMethod (ここでも重要)  ModelAndViewContainer
  • 130. 関係図 (C) CASAREAL, Inc. All rights reserved. 130 HandlerMethodReturnValueHandler
  • 131. HandlerMethodReturnValueHandler (C) CASAREAL, Inc. All rights reserved. 131  コントローラーメソッドの戻り値を処理する  戻り値の文字列をSpringにView名として認識させる  RESTの場合は JSON への変換処理を呼び出す  インタフェースであり、これを多数のクラスが実装  String  Map  ResponseBody など、各クラス分実装クラスが存在する  Viewに処理を委譲するか、そのままレスポンスするか のフラグの更新も担う
  • 132. HandlerMethodReturnValueHandler (C) CASAREAL, Inc. All rights reserved. 132 public interface HandlerMethodReturnValueHandler { boolean supportsReturnType(MethodParameter returnType); void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }
  • 133. HandlerMethodReturnValueHandler (C) CASAREAL, Inc. All rights reserved. 133  supportsReturnType  メソッドの戻り値の型や付加したアノテーションな どからサポートするかを判定するメソッド  handleReturnValue  メソッドの戻り値を処理するメソッド 以後、ReturnValueHandler と省略した場合は、 こちらを示しています。
  • 134. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 134  HandlerMethodReturnValueHandler  HandlerAdapter(ここでも重要)  ServletInvocableHandlerMethod (ここでも重要)  ModelAndViewContainer
  • 135. 関係図 (C) CASAREAL, Inc. All rights reserved. 135 HandlerAdapter HandlerMethodReturnValueHandler
  • 136. RequestMappingHandlerAdapter (C) CASAREAL, Inc. All rights reserved. 136  HandlerMethod用のHandlerAdapterの実装クラス  HandlerMethodArgumentResolverのインスタンスを保持  ServletInvocableHandlerMethodに HandlerMethodArgumentResolverのインスタンスを渡し て委譲する  HandlerMethodReturnValueHandlerのインスタンスを保持
  • 137. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 137  HandlerMethodReturnValueHandler  HandlerAdapter(ここでも重要)  ServletInvocableHandlerMethod (ここでも重要)  ModelAndViewContainer
  • 138. 関係図 (C) CASAREAL, Inc. All rights reserved. 138 HandlerAdapter HandlerMethodReturnValueHandler ServletInvocableHandlerMethod (リクエストごとに作られる)
  • 139. ServletInvocableHandlerMethod (C) CASAREAL, Inc. All rights reserved. 139  HandlerMethodを継承している  RequestMappingHandlerAdapterから借り受けた HandlerMethodArgumentResolverを使って引数のインス タンスを調達する  コントローラーメソッドを実行する  HandlerMethodReturnValueHandlerを使って、戻り値の 処理を行う
  • 140. 関係図 (C) CASAREAL, Inc. All rights reserved. 140 RequestMapping HandlerAdapter ServletInvocable HandlerMethod this.returnValueHandlers setHandlerMethodReturnValueHandlers これも使って! 任せとけ! リクエストごとに new
  • 141. 押さえておきたい登場人物 (C) CASAREAL, Inc. All rights reserved. 141  HandlerMethodReturnValueHandler  HandlerAdapter(ここでも重要)  ServletInvocableHandlerMethod (ここでも重要)  ModelAndViewContainer
  • 142. ModelAndViewContainer (C) CASAREAL, Inc. All rights reserved. 142  ModelとView名を保持するクラス  DispatcherServletがViewを見つけ出す橋渡し役でもある  Viewに処理を委譲する or そのままレスポンスする を振り分けるフラグをもつ  requestHandledフィールドが該当
  • 143. ServletInvocableHandlerMethod#invokeAndHandle() (C) CASAREAL, Inc. All rights reserved. 143 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 中略 mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); HandlerMethod実行後 の戻り値を受け取る
  • 144. ServletInvocableHandlerMethod#invokeAndHandle() (C) CASAREAL, Inc. All rights reserved. 144 try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } } 戻り値の調整を 行う
  • 145. 【例】コントローラークラスの定義 (C) CASAREAL, Inc. All rights reserved. 145 @Controller public class TestController { @GetMapping("/") public String index(Model model) { model.addAttribute("name", "masato"); model.addAttribute("age", 1); return "test"; } @GetMapping("/test") public Map test2() { return Map.of("name", "masato", "age", 1); } } 戻り値の型が違うが どちらも最終的に “test.html” を出力
  • 146. 動作概要 (C) CASAREAL, Inc. All rights reserved. 146 Map担当 String担当 ResponseBody担当 戻り値の型 ReturnValueHandlerたち ?
  • 147. (C) CASAREAL, Inc. All rights reserved. 147 Map担当 String担当 ResponseBody担当 戻り値の型 String 戻り値の型がString 俺の出番 だな! サポート不可。。
  • 148. (C) CASAREAL, Inc. All rights reserved. 148 Map担当 String担当 ResponseBody担当 戻り値の型 Map 戻り値の型がMap 任せとけ! サポート不可。。
  • 149. (C) CASAREAL, Inc. All rights reserved. 149 Map担当 String担当 ResponseBody担当 戻り値の型 ResponseBody 戻り値の型がResponseBody 任せとけ! できない!
  • 150. 戻り値に指定可能な型は? (C) CASAREAL, Inc. All rights reserved. 150  指定可能な型が多すぎる https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring- framework-reference/web.html#mvc-ann-return-types  丸暗記ではなく取捨選択が重要  ソースコード上からも指定可能な型を確認できる
  • 151. RequestMappingHandlerAdapter#getDefaultReturnValueHandlers() (C) CASAREAL, Inc. All rights reserved. 151 private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(); // Single-purpose return value types handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(), this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager)); handlers.add(new StreamingResponseBodyReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
  • 152. RequestMappingHandlerAdapter#getDefaultArgumentResolvers() (C) CASAREAL, Inc. All rights reserved. 152 handlers.add(new HttpHeadersReturnValueHandler()); handlers.add(new CallableMethodReturnValueHandler()); handlers.add(new DeferredResultMethodReturnValueHandler()); handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); // Annotation-based return value types handlers.add(new ModelAttributeMethodProcessor(false)); handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor());
  • 153. RequestMappingHandlerAdapter#getDefaultArgumentResolvers() (C) CASAREAL, Inc. All rights reserved. 153 // Custom return value types if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) { handlers.add(new ModelAndViewResolverMethodReturnValueHandler( getModelAndViewResolvers())); } else { handlers.add(new ModelAttributeMethodProcessor(true)); } return handlers; }
  • 155. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 155  Viewに委譲するパターン  String  Map  Viewに委譲しないパターン(REST)  ResponseBody  ResponseEntity
  • 156. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 156  Viewに委譲するパターン  String  Map  Viewに委譲しないパターン(REST)  ResponseBody  ResponseEntity
  • 157. ViewNameMethodReturnValueHandler (C) CASAREAL, Inc. All rights reserved. 157  戻り値を文字列にした場合が該当する  戻り値をModelAndViewContainerが持つView名として セットする
  • 158. (C) CASAREAL, Inc. All rights reserved. 158 public boolean supportsReturnType(MethodParameter returnType) { Class<?> paramType = returnType.getParameterType(); return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType)); } ViewNameMethodReturnValueHandler#supportsReturnType() 意外にもサポート対象の型に CharSequenceだけでなく voidも含まれる
  • 159. ViewNameMethodReturnValueHandler#handleReturnValue() (C) CASAREAL, Inc. All rights reserved. 159 public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (returnValue instanceof CharSequence) { String viewName = returnValue.toString(); mavContainer.setViewName(viewName); if (isRedirectViewName(viewName)) { mavContainer.setRedirectModelScenario(true); } } 戻り値をViewName としてセット
  • 160. なぜvoidもサポート対象なのか? (C) CASAREAL, Inc. All rights reserved. 160  明示しない場合、リクエストURLが論理View名となる  http://localhost:8080/test とアクセスした場合、 論理View名に “test” を指定した動作になる
  • 161. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 161  Viewに委譲するパターン  String  Map  Viewに委譲しないパターン(REST)  ResponseBody  ResponseEntity
  • 162. MapMethodProcessor (C) CASAREAL, Inc. All rights reserved. 162  戻り値をMap型にした場合が該当する  戻り値をModelAndViewContainerの Modelにすべて追加する
  • 163. (C) CASAREAL, Inc. All rights reserved. 163 public boolean supportsReturnType(MethodParameter returnType) { return Map.class.isAssignableFrom(returnType.getParameterType()); } MapMethodProcessor#supportsReturnType() 戻り値がMap型がサポート対象
  • 164. (C) CASAREAL, Inc. All rights reserved. 164 public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (returnValue instanceof Map){ mavContainer.addAllAttributes((Map) returnValue); } } MapMethodProcessor#handleReturnValue() 戻り値のMapの値をすべて Modelに追加している
  • 165. 【例】コントローラークラスの定義 (C) CASAREAL, Inc. All rights reserved. 165 @Controller public class TestController { @GetMapping("/") public String index(Model model) { model.addAttribute("name", "masato"); model.addAttribute("age", 1); return "test"; } @GetMapping("/test") public Map test2() { return Map.of("name", "masato", "age", 1); } } 二つのメソッドは最終的に 同じ挙動になる
  • 166. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 166  Viewに委譲するパターン  String  Map  Viewに委譲しないパターン(REST)  ResponseBody  ResponseEntity
  • 167. RequestResponseBodyMethodProcessor (C) CASAREAL, Inc. All rights reserved. 167  @ResponseBodyが付加されている場合が該当する  クラス、メソッドどちらにも付加可能  @RestControllerを付加している場合も該当  @Controllerと@ResponseBodyを合成したもの  戻り値のJavaオブジェクトをJSONに変換して レスポンス
  • 168. (C) CASAREAL, Inc. All rights reserved. 168 public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation( returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); } RequestResponseBodyMethodProcessor#supportsReturnType() メソッド、もしくはクラスに @ResponseBodyが付加されてい る場合サポート対象
  • 169. (C) CASAREAL, Inc. All rights reserved. 169 public void handleReturnValue( @Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // Try even with null return value. ResponseBodyAdvice could get involved. writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); } RequestResponseBodyMethodProcessor#handleReturnValue() そのままレスポンスする設定 JSONに変換してレスポンスする処理
  • 170. いくつか絞ってご紹介 (C) CASAREAL, Inc. All rights reserved. 170  Viewに委譲するパターン  String  Map  Viewに委譲しないパターン(REST)  ResponseBody  ResponseEntity
  • 171. HttpEntityMethodProcessor (C) CASAREAL, Inc. All rights reserved. 171  戻り値をHttpEntity型もしくはResponseEntity型 にした場合が該当  ResponseEntity型の場合レスポンスステータスをセット  戻り値のbodyにセットしたJavaオブジェクトをJSONに 変換してレスポンス
  • 172. (C) CASAREAL, Inc. All rights reserved. 172 public boolean supportsReturnType(MethodParameter returnType) { return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) && !RequestEntity.class.isAssignableFrom(returnType.getParameterType())); } HttpEntityMethodProcessor#supportsReturnType() HttpEntity型およびResponseEntity型 がサポート対象
  • 173. (C) CASAREAL, Inc. All rights reserved. 173 public void handleReturnValue( @Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); // 中略 if (responseEntity instanceof ResponseEntity) { int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue(); outputMessage.getServletResponse().setStatus(returnStatus); // JSONに変換する処理の呼び出し } HttpEntityMethodProcessor#handleReturnValue() そのままレスポンスする設定 レスポンスステータスの設定
  • 174. (C) CASAREAL, Inc. All rights reserved. 174
  • 175. 目次 (C) CASAREAL, Inc. All rights reserved. 175 1.Spring MVC のアーキテクチャー 2.URLとコントローラーメソッド 3.コントローラーメソッドの引数 4.コントローラーメソッドの戻り値 5.全体を俯瞰
  • 176. Spring MVC のアーキテクチャー 再掲 (C) CASAREAL, Inc. All rights reserved. 176 Dispatcher Servlet Handler Mapping Handler Adapter View Resolver Model Controller view name View 1 2 3 4 56
  • 177. DispatcherServlet#doDispatch() が Spring MVC の骨格です (C) CASAREAL, Inc. All rights reserved. 177
  • 178. Spring MVC のアーキテクチャー (C) CASAREAL, Inc. All rights reserved. 178 Dispatcher Servlet Handler Mapping Controller 1
  • 179. (C) CASAREAL, Inc. All rights reserved. 179 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine handler for the current request. mappedHandler = getHandler(processedRequest); DispatcherServlet#doDispatch() HandlerMapping呼び出し
  • 180. (C) CASAREAL, Inc. All rights reserved. 180 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; } DispatcherServlet#getHandler() HandlerMapping呼び出し
  • 181. Spring MVC のアーキテクチャ (C) CASAREAL, Inc. All rights reserved. 181 Dispatcher Servlet Handler Adapter Controller view name 2 3 4
  • 182. (C) CASAREAL, Inc. All rights reserved. 182 // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); DispatcherServlet#doDispatch() HandlerAdapterの取り出し コントローラーメソッドの実行
  • 183. Spring MVC のアーキテクチャー (C) CASAREAL, Inc. All rights reserved. 183 Dispatcher Servlet View Resolver Model View 56
  • 184. (C) CASAREAL, Inc. All rights reserved. 184 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); DispatcherServlet#doDispatch() Viewの解決とレンダリング処理
  • 185. RESTの場合 (C) CASAREAL, Inc. All rights reserved. 185
  • 186. (C) CASAREAL, Inc. All rights reserved. 186 private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); // 中略 return mav; } requestHandledがtrueの場合は、 DispatcherServletに ModelAndViewを返さない RequestMappingHandlerAdapter#getModelAndView()
  • 187. まとめ (C) CASAREAL, Inc. All rights reserved. 187  基本となるインタフェースと実装クラスを 抑えれば読解は可能  引数のインスタンスの生成順序を正確に 把握できるのでトラブルに強くなれる  initModelやinterceptorなど紹介していない 処理の呼び出しも是非読んでいただきたい
  • 188. 今後 (C) CASAREAL, Inc. All rights reserved. 188  RequestMappingInfoの生成過程など、 より詳しく知るためにはDIコンテナの 生成過程を知る必要がある  機会があればアプリの起動時の処理を紹介 したい