$30 off During Our Annual Pro Sale. View Details »

こわくないソースコードリーディング生活 / JJUG CCC 2019 Fall

Ryo Shindo
November 23, 2019

こわくないソースコードリーディング生活 / JJUG CCC 2019 Fall

JJUG CCC 2019 Fall #ccc_c5

Ryo Shindo

November 23, 2019
Tweet

More Decks by Ryo Shindo

Other Decks in Programming

Transcript

  1. 1 こわくない ソースコードリーディング生活 JJUG CCC 2019 Fall #ccc_c5 Acroquest Technology株式会社

    進藤 遼 (@shindo_ryo) Copyright © Acroquest Technology Co., Ltd. All rights reserved.
  2. 自己紹介 • 進藤 遼 • Acroquest Technology株式会社 • 日本Springユーザ会 スタッフ

    • Twitter: @shindo_ryo • Spring / JUnit 5 / Microservices • 最近はエンプラ系システムでアーキテクチャ設計やったり Goで分散トレーシングやったり。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 2
  3. Acroquestのミッション・ビジョン Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    3 テクノロジストチームとして ビジネスの革新的価値創出に挑戦する ビジョン Acroquestの創り出す技術で 地球を感動で進化させる ミッション
  4. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 6

    他人が書いたコード 読んだことがある人?
  5. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 7

    OSSライブラリのコード 読んだことがある人?
  6. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 8

    Webフレームワークのコード 読んだことがある人? はじめに
  7. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 13

    OSSのソースコードを 読むのはハードルが高い …ホントに?
  8. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 14

    OSSのソースコードを 読むのはハードルが高い 高い気がする
  9. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 15

    読み方を知れば ソースコードリーディングは こわくない
  10. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 16

    一歩ずつ、コードを 読めるようになればいい
  11. はじめに • このセッションの対象者 ➢ OSSのソースコードを読んだことが無い ➢ 読んだことはあるがいまいち理解できない • このセッションの目的 ➢

    ソースコードを読むことへのハードルを下げる ➢ ソースコードを効率的に学べるようになる Copyright © Acroquest Technology Co., Ltd. All rights reserved. 17
  12. アジェンダ • STEP 1: ソースコードを開こう! • STEP 2: ライブラリのコードを読もう! •

    STEP 3: 構造を読み解こう! • Appendix: ソースコードリーディングは 心がジーンとする Copyright © Acroquest Technology Co., Ltd. All rights reserved. 18
  13. STEP 1: ソースコードを開こう! • 例えばこんなコードがあったりする・・・ Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 20 if (StringUtils.isEmpty("")) { System.out.println("JJUG CCC 2019 Fall"); } Apache Commons の org.apache.commons.lang3.StringUtils
  14. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 21

    そもそもコード読むのに 何を使ってますか? 0. はじめに
  15. STEP 1: ソースコードを開こう! • ソースコードを読む手段 ① IDE ←オススメ – Eclipse

    / IntelliJ IDEA / NetBeans … お好きなもので – マウスオーバーで変数や引数の型を確認したり、 Ctrl + 左クリックで型ジャンプができる ② GitHub – ソースコードを取得する必要がないので手軽 – 複数のファイルの行ったり来たりしようとすると、途端に辛くなる (特にパッケージ階層をまたがると辛い) ③ テキストエディタ – 止めはしない。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 22
  16. STEP 1: ソースコードを開こう! • ソースコードを取得する方法 ① Maven or Gradleで *-sources.jar

    をDL – IDEだとクリックだけで簡単にダウンロードできる – 使っているバージョンと正確に一致したソースコードを確認できる – 自分が書いたコードからライブラリのコードに直接ジャンプできる ② git clone – ソースコードだけでなく、READMEやテストコードも取れる – 大きなリポジトリは大体git cloneで取ってきてます – 別バージョンのソースコードを取ってこないように注意 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 23
  17. STEP 1: ソースコードを開こう! • 再掲 Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 24 if (StringUtils.isEmpty("")) { System.out.println("JJUG CCC 2019 Fall"); } Apache Commons の org.apache.commons.lang3.StringUtils
  18. STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 25 Q. このメソッドの中身は どうなっているでしょうか?
  19. STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 26 public static boolean isEmpty(final CharSequence cs) { return cs == null || cs.length() == 0; } A.
  20. STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 27 public static boolean isEmpty(final CharSequence cs) { return cs == null || cs.length() == 0; } 引数のcsがnull またはcsの長さが0
  21. STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 28 こんな簡単なコードから始めるの? って思いました?
  22. STEP 1: ソースコードを開こう! • ライブラリのソースコードを読むことはハードル高くない ➢ 大部分のコードは、1行1行は難しいこと書いてない ➢ Javaの文法と基本的なAPIを抑えていれば、Apache Commons

    程度であれば大体読める ➢ きちんとしたライブラリであればJavadocに仕様はちゃんと 書いてある Copyright © Acroquest Technology Co., Ltd. All rights reserved. 30
  23. STEP 1: ソースコードを開こう! • なぜソースコードリーディングを難しく感じるのか • 多重知能理論 • 言語的知能 •

    論理数学的知能 • 音楽的知能 • 身体運動的知能 • 空間的知能 • 対人的知能 • 内省的知能 • 博物的知能 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 31 http://www.nichinoken.co.jp/np5/nnk/multiple_intelligences/mi/mi.html
  24. STEP 1: ソースコードを開こう! • ソースコードリーディングは知識・論理・読解の複合スキル • 知識 ・・・ CS、Web、API、etc… •

    論理 ・・・ ロジック、アルゴリズムの理解 • 読解 ・・・ 文法の適用、 文脈を理解して適度に必要のない情報を無視する (英語力も?) Copyright © Acroquest Technology Co., Ltd. All rights reserved. 32 知識 論理 読解 ソースコード リーディング
  25. STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 33 ソースコードリーディング = スキル
  26. STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 34 スキルはトレーニングによって身に付く
  27. STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 37 知らなかったことを知る 読めなかったものが読めるようになる
  28. STEP 2: ライブラリのコードを読もう! • コードを読む上で大事なこと 1. 目的、目標を設定する (重要) ➢ これがないと読むべきところ、読み飛ばして良いところの区別がつかない

    2. 深く掘り下げすぎない ➢ 記憶はスタック構造。詳細を見すぎると全体像を見失う 3. クラスの使い方を把握する ➢ Hello worldで良いので、どんな機能か、呼び出し元はどう書くかを調べる 4. 名前に注意する ➢ クラス名・変数名・メソッド名の意味を理解することで仕様を理解する Copyright © Acroquest Technology Co., Ltd. All rights reserved. 40
  29. STEP 2: ライブラリのコードを読もう! • Commons DBUtils • 軽量なRDBアクセスライブラリ • 生JDBCドライバを使ったコードに比べて

    簡潔に記述することができる • QueryRunnerクラスのquery(), insert(), update() といった メソッドを呼び出し、ResultSetHandlerインタフェース(とその実装クラ ス)を使って結果を特定の型にマッピングする。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 41
  30. STEP 2: ライブラリのコードを読もう! • 読むのは以下のコード Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 42 DataSource ds = … QueryRunner queryRunner = new QueryRunner(ds); List<User> result = queryRunner.query( "SELECT * FROM users;", new BeanListHandler<>(User.class)); RDBへの接続を作るファクトリ。 大抵の場合コネクションプール。 usersテーブルからデータを取得して、 各レコードをUser型に変換したリスト を作成する。
  31. STEP 2: ライブラリのコードを読もう! • 目標を設定する ✓ QueryRunnerがJDBCとどう連携しているかを理解する ✓ コネクションをいつ取得して、いつクローズしているかを確認する ✓

    usersテーブルの”first_name“をUserクラスの”firstName”に マッピングできるかどうかを調べる Copyright © Acroquest Technology Co., Ltd. All rights reserved. 43 これを目標にして ソースコードの読解を進める
  32. STEP 2: ライブラリのコードを読もう! • QueryRunner#query() Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 45 引数のnullチェック 例外処理 これらは一旦無視
  33. STEP 2: ライブラリのコードを読もう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 46 _人人人人_ > 無視 <  ̄Y^Y^Y^ ̄
  34. STEP 2: ライブラリのコードを読もう! • QueryRunner#query() Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 49 stmt = this.prepareStatement(conn, sql); this.fillStatement(stmt, params); rs = this.wrap(stmt.executeQuery()); result = rsh.handle(rs); PreparedStatementの作成 SQSにクエリパラメータをセット クエリを実行 <T> 型 ResultSetを <T>型の変数に変換
  35. STEP 2: ライブラリのコードを読もう! • BeanListHandler Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 51 public class BeanListHandler<T> implements ResultSetHandler<List<T>> { private final Class<? extends T> type; private final RowProcessor convert; public BeanListHandler(Class<? extends T> type) { this(type, ArrayHandler.ROW_PROCESSOR); } @Override public List<T> handle(ResultSet rs) throws SQLException { return this.convert.toBeanList(rs, type); } Bean変換は convert が行っ ている。 convertの中身は BasicRowProcessor
  36. STEP 2: ライブラリのコードを読もう! • BeanProcessor Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 52 public <T> List<T> toBeanList(ResultSet rs, Class<? extends T> type) throws SQLException { List<T> results = new ArrayList<T>(); if (!rs.next()) { return results; } PropertyDescriptor[] props = this.propertyDescriptors(type); ResultSetMetaData rsmd = rs.getMetaData(); int[] columnToProperty = this.mapColumnsToProperties(rsmd, props); do { results.add(this.createBean(rs, type, props, columnToProperty)); } while (rs.next()); return results; } BasicRowProcessorのtoBeanList(rs, type)は BeanProcessorに処理を移譲するだけ PropertyDescriptorはBeanの プロパティをgetter/setter経由 でアクセスするためのクラス
  37. STEP 2: ライブラリのコードを読もう! • BeanProcessor Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 53 public <T> List<T> toBeanList(ResultSet rs, Class<? extends T> type) throws SQLException { List<T> results = new ArrayList<T>(); if (!rs.next()) { return results; } PropertyDescriptor[] props = this.propertyDescriptors(type); ResultSetMetaData rsmd = rs.getMetaData(); int[] columnToProperty = this.mapColumnsToProperties(rsmd, props); do { results.add(this.createBean(rs, type, props, columnToProperty)); } while (rs.next()); return results; } BasicBeanProcessorのtoBeanList(rs, type)は BeanProcessorに処理を移譲するだけ 結果セットのカラム名を取得する ここが怪しい
  38. STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 54 やっていることはResultSetの各 カラムの値が、Beanのどの PropertyDescriptorに格納され るかの対応づけ
  39. STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) ➢ JavaDocも読んで見る ◆ 戻り値の配列 (int配列)

    の位置はカラム番号を表します。各位置に 格納されてる値は、そのカラム名に対応したBeanプロパティの PropertyDescriptor[]の位置を表します。カラムに対応するBean プロパティが存在しない場合は、その位置には PROPERTY_NOT_FOUND (-1) がセットされます。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 55 戻り値のint配列に値がセットされているところを見れば、 対応付けの方法がわかりそう
  40. STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 57 columnToPropertyOverridesにcolumnNameに 対応したpropertyNameがあれば取得して、 なければcolumnNameをpropertyNameに使って HashMap<String, String> デフォルトでは空
  41. STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 58 PropertyDescriptor配列をぐるぐる回して、 プロパティ名が一致するものを探す (大文字小文字の違いは無視)
  42. STEP 2: ライブラリのコードを読もう! • 結論 ➢ 元のコードでは、“first_name”を”firstName”にマッピングしてくれな い。 ➢ BeanProcessorのcolumnToPropertyOverrides

    で”first_name”と”firstName”の対応を指定すれば マッピングできそう。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 59 Commons DBUtils完全にマスターした
  43. STEP 3: 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 61 Webフレームワークのソースコード、 読みたくなりません? 読みたいですよね?
  44. STEP 3: 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 62 でもJavaのWebフレームワークって 大体10KL以上で読むの辛いし 多機能すぎるし・・・
  45. STEP 3: 構造を読み解こう! • Javalin • シンプル・軽量なWebフレームワーク • Jettyを内包しており、single jarで動作する

    • WebSocket, バリデーション、Server-Sent Eventsなど 基本的な機能は一通りそろっている • JavaだけでなくKotlinも混ざっているけどキニシナイ! Copyright © Acroquest Technology Co., Ltd. All rights reserved. 64
  46. STEP 3: 構造を読み解こう! • Java Servlet (Jakarta Servlet) • JavaでWebアプリケーションをつくるための仕様

    (と作成されたプログラム) • HttpServletクラスを継承し、doGet/doPost/doPut/… メソッドに HTTPリクエストを受け付けた際の処理を書く • Servletを動作させるにはサーブレットコンテナ(Tomcat, Jetty, etc…)が必要 • Jetty • サーブレットコンテナの一種 • Tomcatに比べてサイズが小さく起動が早い Copyright © Acroquest Technology Co., Ltd. All rights reserved. 65
  47. STEP 3: 構造を読み解こう! • コードを読む上で大事なこと (再掲) 1. 目的、目標を設定する ➢ これがないと読むべきところ、読み飛ばして良いところの区別がつかない

    2. 深く掘り下げすぎない ➢ 記憶はスタック構造。詳細を見すぎると全体像を見失う 3. クラスの使い方を把握する ➢ Hello worldで良いので、どんな機能か、呼び出し元はどう書くかを調べる 4. 名前に注意する ➢ クラス名・変数名・メソッド名の意味を理解することで仕様を理解する Copyright © Acroquest Technology Co., Ltd. All rights reserved. 66
  48. STEP 3: 構造を読み解こう! • コードを読む上で大事なこと② 1. 型を強く意識する ➢ 各クラス・インタフェースの役割を把握することで、クラス間の関係が 非常に理解しやすくなる

    2. メモする ➢ 大きなコードを読むときに現在位置を把握しておく 3. ドキュメント、Javadocをよく読む ➢ 仕様だけでなく実装の解説も書いてあったりする Copyright © Acroquest Technology Co., Ltd. All rights reserved. 67
  49. STEP 3: 構造を読み解こう! • JavalinでシンプルなWebアプリケーション Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 68 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start(); たった3行でWebアプリケーションの 起動ができる
  50. STEP 3: 構造を読み解こう! • まずはJavadocを読む Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 69 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start(); Javalinインスタンスの生成 /hello にハンドラを登録 サーバーの起動
  51. STEP 3: 構造を読み解こう! • 上から順に読み進めていく Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 70 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start(); まずは全体像の理解から進める。 ※各メソッドで深追いしすぎないように注意
  52. STEP 3: 構造を読み解こう! • Javalin#create() Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 71 public static Javalin create() { return create(JavalinConfig.noopConfig); } public static Javalin create(Consumer<JavalinConfig> config) { Javalin app = new Javalin(); JavalinConfig.applyUserConfig(app, app.config, config); if (app.config.logIfServerNotStarted) { Util.logIfServerNotStarted(app.server); } return app; }
  53. STEP 3: 構造を読み解こう! • Javalin コンストラクタ Copyright © Acroquest Technology

    Co., Ltd. All rights reserved. 72 protected JavalinServer server; protected JavalinWsServlet wsServlet; protected JavalinServlet servlet = new JavalinServlet(config); protected Javalin() { this.server = new JavalinServer(config); this.wsServlet = new JavalinWsServlet(config); } サーバーっぽい何か Servletっぽい何か WebSocketっぽいので一旦無視
  54. STEP 3: 構造を読み解こう! • 上から順に読み進めていく Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 73 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start();
  55. STEP 3: 構造を読み解こう! • Javalin#get(…) Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 74 public Javalin get(@NotNull String path, @NotNull Handler handler) { return addHandler(HandlerType.GET, path, handler); } public Javalin addHandler(@NotNull HandlerType handlerType, @NotNull String path, @NotNull Handler handler, @NotNull Set<Role> roles) { servlet.addHandler(handlerType, path, handler, roles); eventManager.fireHandlerAddedEvent( new HandlerMetaInfo(handlerType, Util.prefixContextPath(servlet.getConfig().contextPath, path), handler, roles)); return this; } ラムダ式はHandler型のインスタンスになる JavalinServlet#addHandlerに移譲 イベントハンドラに発火 とりあえず無視
  56. STEP 3: 構造を読み解こう! • 上から順に読み進めていく Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 75 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start();
  57. STEP 3: 構造を読み解こう! • Javalin#start() Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 76 server.start(servlet, wsServlet); これ以外は とりあえず無視
  58. STEP 3: 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 80 ※ちなみにここからKotlinのコードが 混ざり始めます 僕も文法ちゃんと覚えてないからヘーキヘーキ
  59. STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 81 fun addHandler(handlerType: HandlerType, path: String, handler: Handler, roles: Set<Role>) { val shouldWrap = handlerType.isHttpMethod() && !roles.contains(CoreRoles.NO_WRAP) val protectedHandler = if (shouldWrap) Handler { ctx -> config.inner.accessManager.manage(handler, ctx, roles) } else handler matcher.add(HandlerEntry(handlerType, path, protectedHandler, handler)) } 1, 2行目はアクセス制御 PathMatcher HandlerEntryはpathとhandler をセットにしたクラス
  60. STEP 3: 構造を読み解こう! • PathMatcher.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 82 fun add(entry: HandlerEntry) { if (entry.type.isHttpMethod() && handlerEntries[entry.type]!!.find { it.type == entry.type && it.path == entry.path } != null) { throw IllegalArgumentException("Handler with type='${entry.type}’ and path='${entry.path}' already exists.") } handlerEntries[entry.type]!!.add(entry) } 同じパスに対して複数の ハンドラが登録されたら 例外を投げる private val handlerEntries = HandlerType.values().associateTo( EnumMap<HandlerType, ArrayList<HandlerEntry>>(HandlerType::class.java)) { it to arrayListOf() }
  61. STEP 3: 構造を読み解こう! • handlerEntriesのデータ構造 handlerEntries: GET: [{path: ”/hello”, handler:

    …}, {path: “/bye”, handler: …}], POST: [{path: “/echo”, handler: …}], PUT: … … Copyright © Acroquest Technology Co., Ltd. All rights reserved. 83 HTTPメソッド + αがkey List<HandlerEntry>がvalue
  62. STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ➢ ハンドラのリストが、HandlerTypeをキーとしたMapの

    Valueに格納されている ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている? Copyright © Acroquest Technology Co., Ltd. All rights reserved. 84
  63. STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ➢ ハンドラのリストが、HandlerTypeをキーとしたMapの

    Valueに格納されている ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている? Copyright © Acroquest Technology Co., Ltd. All rights reserved. 85
  64. STEP 3: 構造を読み解こう! • JavalinServer.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 86 httpHandlerという変数名に注目して この部分を集中して読む
  65. STEP 3: 構造を読み解こう! • JavalinServer.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 87 ServletContextHandlerはServletHandlerや SessionHandlerなどを集約したJettyのクラス ServletContextHandlerの匿名クラス “/*” で全てのパスに対して、 JavalinServletを登録している
  66. STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 88 class JavalinServlet(val config: JavalinConfig) : HttpServlet() { JavalinServlet は HttpServletを 継承したクラス
  67. STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 89 HttpServlet#service メソッドのJavadoc 「public で定義された service メソッド経由で標準 HTTP リクエスト を受け取り、それらをこのクラスで定義された doXXX メソッドに ディスパッチします。」
  68. STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 90 fun tryBeforeAndEndpointHandlers() = tryWithExceptionMapper { (中略) matcher.findEntries(type, requestUri).firstOrNull()?.let { entry -> entry.handler.handle( ContextUtil.update(ctx, entry, requestUri)) return@tryWithExceptionMapper // return after first match } serviceメソッドから抜粋
  69. STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 91 fun tryBeforeAndEndpointHandlers() = tryWithExceptionMapper { (中略) matcher.findEntries(type, requestUri).firstOrNull()?.let { entry -> entry.handler.handle( ContextUtil.update(ctx, entry, requestUri)) return@tryWithExceptionMapper // return after first match } PathMatcher handlerEntriesを保持
  70. STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 92 fun tryBeforeAndEndpointHandlers() = tryWithExceptionMapper { (中略) matcher.findEntries(type, requestUri).firstOrNull()?.let { entry -> entry.handler.handle( ContextUtil.update(ctx, entry, requestUri)) return@tryWithExceptionMapper // return after first match } Type (HTTPメソッド) とURLにマッチ するHandlerEntryを探索 fun findEntries(handlerType: HandlerType, requestUri: String) = handlerEntries[handlerType]!!.filter { he -> match(he, requestUri) } 線形に探索している
  71. STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 93 fun tryBeforeAndEndpointHandlers() = tryWithExceptionMapper { (中略) matcher.findEntries(type, requestUri).firstOrNull()?.let { entry -> entry.handler.handle( ContextUtil.update(ctx, entry, requestUri)) return@tryWithExceptionMapper // return after first match } Handlerの実行
  72. STEP 3: 構造を読み解こう! • HTTPリクエストを受け付ける流れ Copyright © Acroquest Technology Co.,

    Ltd. All rights reserved. 95 JavalinServlet が全てのリクエストを受け付けて Handlerに処理を振り分けている
  73. STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ➢ ハンドラのリストが、HandlerTypeをキーとしたMapの

    Valueに格納されている ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている? ➢ JavalinServletが全てのリクエストを受け付けて、パスに応じて 各Handlerに処理を振り分けている Copyright © Acroquest Technology Co., Ltd. All rights reserved. 96
  74. STEP 3: 構造を読み解こう! • まとめ ➢ 浅く広く読んでから、少しずつ深いところまで読み進める ➢ ときどき図に書いて整理すると構造を理解しやすい ➢

    目標と関係ないコードを読み飛ばすの大事 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 97 Javalin完全に(以下略)
  75. 余談 • Sourcetrail • Coati Softwareが開発する コードエクスプローラ • 数日前にオープンソース化された •

    クラスの依存関係、 呼び出し階層などを グラフィカルに表示できる • これを使うとコード読むのが捗るかも? Copyright © Acroquest Technology Co., Ltd. All rights reserved. 98 https://www.sourcetrail.com/
  76. ソースコードリーディングは心がジーンとする Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    100 僕のトロが言ってました 「にゃるほど、ソースコードリーディングは 心がジーンとする!」
  77. ソースコードリーディングは心がジーンとする • 良いコードとは? ✓ 読みやすい ✓ 適切に抽象化されている ✓ 責務が明確 ✓

    Javadoc、コメントが過不足ない ⇒ 良いコードを読むことは、これらのサンプルを知ること Copyright © Acroquest Technology Co., Ltd. All rights reserved. 101
  78. ソースコードリーディングは心がジーンとする Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    102 コードをたくさん読むことで 良いコードの審美眼を鍛える