Share
Embed
Transcript
105 slides
-
1 こわくない ソースコードリーディング生活 JJUG CCC 2019 Fall #ccc_c5 Acroquest Technology株式会社
進藤 遼 (@shindo_ryo) Copyright © Acroquest Technology Co., Ltd. All rights reserved. -
自己紹介 • 進藤 遼 • Acroquest Technology株式会社 • 日本Springユーザ会 スタッフ
• Twitter: @shindo_ryo • Spring / JUnit 5 / Microservices • 最近はエンプラ系システムでアーキテクチャ設計やったり Goで分散トレーシングやったり。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 2 -
Acroquestのミッション・ビジョン Copyright © Acroquest Technology Co., Ltd. All rights reserved.
3 テクノロジストチームとして ビジネスの革新的価値創出に挑戦する ビジョン Acroquestの創り出す技術で 地球を感動で進化させる ミッション -
おことわり • このセッションでは、僕がOSSのコードを読むときに考えているこ とをお話しします。 • お話ししたことについて「こうやるべきだ!」というわけではなく、 参考程度に聞いていただければ幸いです。 Copyright © Acroquest
Technology Co., Ltd. All rights reserved. 4 -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 6
他人が書いたコード 読んだことがある人? -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 7
OSSライブラリのコード 読んだことがある人? -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 8
Webフレームワークのコード 読んだことがある人? はじめに -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 13
OSSのソースコードを 読むのはハードルが高い …ホントに? -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 14
OSSのソースコードを 読むのはハードルが高い 高い気がする -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 15
読み方を知れば ソースコードリーディングは こわくない -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 16
一歩ずつ、コードを 読めるようになればいい -
はじめに • このセッションの対象者 ➢ OSSのソースコードを読んだことが無い ➢ 読んだことはあるがいまいち理解できない • このセッションの目的 ➢
ソースコードを読むことへのハードルを下げる ➢ ソースコードを効率的に学べるようになる Copyright © Acroquest Technology Co., Ltd. All rights reserved. 17 -
アジェンダ • STEP 1: ソースコードを開こう! • STEP 2: ライブラリのコードを読もう! •
STEP 3: 構造を読み解こう! • Appendix: ソースコードリーディングは 心がジーンとする Copyright © Acroquest Technology Co., Ltd. All rights reserved. 18 -
STEP 1 ライブラリのコードを読もう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 19 -
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 -
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 21
そもそもコード読むのに 何を使ってますか? 0. はじめに -
STEP 1: ソースコードを開こう! • ソースコードを読む手段 ① IDE ←オススメ – Eclipse
/ IntelliJ IDEA / NetBeans … お好きなもので – マウスオーバーで変数や引数の型を確認したり、 Ctrl + 左クリックで型ジャンプができる ② GitHub – ソースコードを取得する必要がないので手軽 – 複数のファイルの行ったり来たりしようとすると、途端に辛くなる (特にパッケージ階層をまたがると辛い) ③ テキストエディタ – 止めはしない。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 22 -
STEP 1: ソースコードを開こう! • ソースコードを取得する方法 ① Maven or Gradleで *-sources.jar
をDL – IDEだとクリックだけで簡単にダウンロードできる – 使っているバージョンと正確に一致したソースコードを確認できる – 自分が書いたコードからライブラリのコードに直接ジャンプできる ② git clone – ソースコードだけでなく、READMEやテストコードも取れる – 大きなリポジトリは大体git cloneで取ってきてます – 別バージョンのソースコードを取ってこないように注意 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 23 -
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 -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 25 Q. このメソッドの中身は どうなっているでしょうか? -
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. -
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 -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 28 こんな簡単なコードから始めるの? って思いました? -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 29 ここから始めます -
STEP 1: ソースコードを開こう! • ライブラリのソースコードを読むことはハードル高くない ➢ 大部分のコードは、1行1行は難しいこと書いてない ➢ Javaの文法と基本的なAPIを抑えていれば、Apache Commons
程度であれば大体読める ➢ きちんとしたライブラリであればJavadocに仕様はちゃんと 書いてある Copyright © Acroquest Technology Co., Ltd. All rights reserved. 30 -
STEP 1: ソースコードを開こう! • なぜソースコードリーディングを難しく感じるのか • 多重知能理論 • 言語的知能 •
論理数学的知能 • 音楽的知能 • 身体運動的知能 • 空間的知能 • 対人的知能 • 内省的知能 • 博物的知能 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 31 http://www.nichinoken.co.jp/np5/nnk/multiple_intelligences/mi/mi.html -
STEP 1: ソースコードを開こう! • ソースコードリーディングは知識・論理・読解の複合スキル • 知識 ・・・ CS、Web、API、etc… •
論理 ・・・ ロジック、アルゴリズムの理解 • 読解 ・・・ 文法の適用、 文脈を理解して適度に必要のない情報を無視する (英語力も?) Copyright © Acroquest Technology Co., Ltd. All rights reserved. 32 知識 論理 読解 ソースコード リーディング -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 33 ソースコードリーディング = スキル -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 34 スキルはトレーニングによって身に付く -
STEP 1: ソースコードを開こう! • トレーニングの成果は負荷(質)と量によって決まる • 簡単なコードだけを大量に読んでも負荷がかからない • 難しすぎるコードも、全く理解できなければ負荷にならない Copyright
© Acroquest Technology Co., Ltd. All rights reserved. 35 -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 36 最初は誰でもつまづく -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 37 知らなかったことを知る 読めなかったものが読めるようになる -
STEP 1: ソースコードを開こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 38 それが成長 -
STEP 2 ライブラリのコードを読もう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 39 -
STEP 2: ライブラリのコードを読もう! • コードを読む上で大事なこと 1. 目的、目標を設定する (重要) ➢ これがないと読むべきところ、読み飛ばして良いところの区別がつかない
2. 深く掘り下げすぎない ➢ 記憶はスタック構造。詳細を見すぎると全体像を見失う 3. クラスの使い方を把握する ➢ Hello worldで良いので、どんな機能か、呼び出し元はどう書くかを調べる 4. 名前に注意する ➢ クラス名・変数名・メソッド名の意味を理解することで仕様を理解する Copyright © Acroquest Technology Co., Ltd. All rights reserved. 40 -
STEP 2: ライブラリのコードを読もう! • Commons DBUtils • 軽量なRDBアクセスライブラリ • 生JDBCドライバを使ったコードに比べて
簡潔に記述することができる • QueryRunnerクラスのquery(), insert(), update() といった メソッドを呼び出し、ResultSetHandlerインタフェース(とその実装クラ ス)を使って結果を特定の型にマッピングする。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 41 -
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型に変換したリスト を作成する。 -
STEP 2: ライブラリのコードを読もう! • 目標を設定する ✓ QueryRunnerがJDBCとどう連携しているかを理解する ✓ コネクションをいつ取得して、いつクローズしているかを確認する ✓
usersテーブルの”first_name“をUserクラスの”firstName”に マッピングできるかどうかを調べる Copyright © Acroquest Technology Co., Ltd. All rights reserved. 43 これを目標にして ソースコードの読解を進める -
STEP 2: ライブラリのコードを読もう! • QueryRunner#query() Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 44 -
STEP 2: ライブラリのコードを読もう! • QueryRunner#query() Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 45 引数のnullチェック 例外処理 これらは一旦無視 -
STEP 2: ライブラリのコードを読もう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 46 _人人人人_ > 無視 <  ̄Y^Y^Y^ ̄ -
STEP 2: ライブラリのコードを読もう! • コードは読み飛ばしていい • 最初から細かいところまで全部読んで、記憶のスタックを使い果たす くらいなら、目標と関係ないところは読み飛ばしたほうがマシ • コードリーディングは、コードの意図や構造を理解する営み。
一字一句音読するものではない。 • 「全部読め」派も特に否定はしないです Copyright © Acroquest Technology Co., Ltd. All rights reserved. 47 -
STEP 2: ライブラリのコードを読もう! • QueryRunner#query() Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 48 この部分を集中 して読む -
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>型の変数に変換 -
STEP 2: ライブラリのコードを読もう! • 一旦図に整理 Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 50 -
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 -
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経由 でアクセスするためのクラス -
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に処理を移譲するだけ 結果セットのカラム名を取得する ここが怪しい -
STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 54 やっていることはResultSetの各 カラムの値が、Beanのどの PropertyDescriptorに格納され るかの対応づけ -
STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) ➢ JavaDocも読んで見る ◆ 戻り値の配列 (int配列)
の位置はカラム番号を表します。各位置に 格納されてる値は、そのカラム名に対応したBeanプロパティの PropertyDescriptor[]の位置を表します。カラムに対応するBean プロパティが存在しない場合は、その位置には PROPERTY_NOT_FOUND (-1) がセットされます。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 55 戻り値のint配列に値がセットされているところを見れば、 対応付けの方法がわかりそう -
STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 56 カラム名を取得して -
STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 57 columnToPropertyOverridesにcolumnNameに 対応したpropertyNameがあれば取得して、 なければcolumnNameをpropertyNameに使って HashMap<String, String> デフォルトでは空 -
STEP 2: ライブラリのコードを読もう! • BeanProcessor#mapColumnsToProperties(…) Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 58 PropertyDescriptor配列をぐるぐる回して、 プロパティ名が一致するものを探す (大文字小文字の違いは無視) -
STEP 2: ライブラリのコードを読もう! • 結論 ➢ 元のコードでは、“first_name”を”firstName”にマッピングしてくれな い。 ➢ BeanProcessorのcolumnToPropertyOverrides
で”first_name”と”firstName”の対応を指定すれば マッピングできそう。 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 59 Commons DBUtils完全にマスターした -
STEP 3 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 60 -
STEP 3: 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 61 Webフレームワークのソースコード、 読みたくなりません? 読みたいですよね? -
STEP 3: 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 62 でもJavaのWebフレームワークって 大体10KL以上で読むの辛いし 多機能すぎるし・・・ -
STEP 3: 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 63 そこで ですよ -
STEP 3: 構造を読み解こう! • Javalin • シンプル・軽量なWebフレームワーク • Jettyを内包しており、single jarで動作する
• WebSocket, バリデーション、Server-Sent Eventsなど 基本的な機能は一通りそろっている • JavaだけでなくKotlinも混ざっているけどキニシナイ! Copyright © Acroquest Technology Co., Ltd. All rights reserved. 64 -
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 -
STEP 3: 構造を読み解こう! • コードを読む上で大事なこと (再掲) 1. 目的、目標を設定する ➢ これがないと読むべきところ、読み飛ばして良いところの区別がつかない
2. 深く掘り下げすぎない ➢ 記憶はスタック構造。詳細を見すぎると全体像を見失う 3. クラスの使い方を把握する ➢ Hello worldで良いので、どんな機能か、呼び出し元はどう書くかを調べる 4. 名前に注意する ➢ クラス名・変数名・メソッド名の意味を理解することで仕様を理解する Copyright © Acroquest Technology Co., Ltd. All rights reserved. 66 -
STEP 3: 構造を読み解こう! • コードを読む上で大事なこと② 1. 型を強く意識する ➢ 各クラス・インタフェースの役割を把握することで、クラス間の関係が 非常に理解しやすくなる
2. メモする ➢ 大きなコードを読むときに現在位置を把握しておく 3. ドキュメント、Javadocをよく読む ➢ 仕様だけでなく実装の解説も書いてあったりする Copyright © Acroquest Technology Co., Ltd. All rights reserved. 67 -
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アプリケーションの 起動ができる -
STEP 3: 構造を読み解こう! • まずはJavadocを読む Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 69 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start(); Javalinインスタンスの生成 /hello にハンドラを登録 サーバーの起動 -
STEP 3: 構造を読み解こう! • 上から順に読み進めていく Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 70 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start(); まずは全体像の理解から進める。 ※各メソッドで深追いしすぎないように注意 -
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; } -
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っぽいので一旦無視 -
STEP 3: 構造を読み解こう! • 上から順に読み進めていく Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 73 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start(); -
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に移譲 イベントハンドラに発火 とりあえず無視 -
STEP 3: 構造を読み解こう! • 上から順に読み進めていく Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 75 Javalin.create() .get("/hello", ctx -> ctx.result("Hello, World!")) .start(); -
STEP 3: 構造を読み解こう! • Javalin#start() Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 76 server.start(servlet, wsServlet); これ以外は とりあえず無視 -
STEP 3: 構造を読み解こう! • メモで整理 Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 77 -
STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている?
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 78 -
STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている?
Copyright © Acroquest Technology Co., Ltd. All rights reserved. 79 -
STEP 3: 構造を読み解こう! Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 80 ※ちなみにここからKotlinのコードが 混ざり始めます 僕も文法ちゃんと覚えてないからヘーキヘーキ -
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 をセットにしたクラス -
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() } -
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 -
STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ➢ ハンドラのリストが、HandlerTypeをキーとしたMapの
Valueに格納されている ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている? Copyright © Acroquest Technology Co., Ltd. All rights reserved. 84 -
STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ➢ ハンドラのリストが、HandlerTypeをキーとしたMapの
Valueに格納されている ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている? Copyright © Acroquest Technology Co., Ltd. All rights reserved. 85 -
STEP 3: 構造を読み解こう! • JavalinServer.kt Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 86 httpHandlerという変数名に注目して この部分を集中して読む -
STEP 3: 構造を読み解こう! • JavalinServer.kt Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 87 ServletContextHandlerはServletHandlerや SessionHandlerなどを集約したJettyのクラス ServletContextHandlerの匿名クラス “/*” で全てのパスに対して、 JavalinServletを登録している -
STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 88 class JavalinServlet(val config: JavalinConfig) : HttpServlet() { JavalinServlet は HttpServletを 継承したクラス -
STEP 3: 構造を読み解こう! • JavalinServlet.kt Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 89 HttpServlet#service メソッドのJavadoc 「public で定義された service メソッド経由で標準 HTTP リクエスト を受け取り、それらをこのクラスで定義された doXXX メソッドに ディスパッチします。」 -
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メソッドから抜粋 -
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を保持 -
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) } 線形に探索している -
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の実行 -
STEP 3: 構造を読み解こう! • クラス・メソッドの関係を図に書いてみる Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 94 -
STEP 3: 構造を読み解こう! • HTTPリクエストを受け付ける流れ Copyright © Acroquest Technology Co.,
Ltd. All rights reserved. 95 JavalinServlet が全てのリクエストを受け付けて Handlerに処理を振り分けている -
STEP 3: 構造を読み解こう! • もう一段深く読み込む ✓ JavalinServlet#addHandler(…)を呼び出したあと、 ハンドラはどんなデータ構造で保持されている? ➢ ハンドラのリストが、HandlerTypeをキーとしたMapの
Valueに格納されている ✓ HTTPリクエストが来たとき、ハンドラはどうやって呼び出されている? ➢ JavalinServletが全てのリクエストを受け付けて、パスに応じて 各Handlerに処理を振り分けている Copyright © Acroquest Technology Co., Ltd. All rights reserved. 96 -
STEP 3: 構造を読み解こう! • まとめ ➢ 浅く広く読んでから、少しずつ深いところまで読み進める ➢ ときどき図に書いて整理すると構造を理解しやすい ➢
目標と関係ないコードを読み飛ばすの大事 Copyright © Acroquest Technology Co., Ltd. All rights reserved. 97 Javalin完全に(以下略) -
余談 • Sourcetrail • Coati Softwareが開発する コードエクスプローラ • 数日前にオープンソース化された •
クラスの依存関係、 呼び出し階層などを グラフィカルに表示できる • これを使うとコード読むのが捗るかも? Copyright © Acroquest Technology Co., Ltd. All rights reserved. 98 https://www.sourcetrail.com/ -
APPENDIX ソースコードリーディングは 心がジーンとする Copyright © Acroquest Technology Co., Ltd. All
rights reserved. 99 -
ソースコードリーディングは心がジーンとする Copyright © Acroquest Technology Co., Ltd. All rights reserved.
100 僕のトロが言ってました 「にゃるほど、ソースコードリーディングは 心がジーンとする!」 -
ソースコードリーディングは心がジーンとする • 良いコードとは? ✓ 読みやすい ✓ 適切に抽象化されている ✓ 責務が明確 ✓
Javadoc、コメントが過不足ない ⇒ 良いコードを読むことは、これらのサンプルを知ること Copyright © Acroquest Technology Co., Ltd. All rights reserved. 101 -
ソースコードリーディングは心がジーンとする Copyright © Acroquest Technology Co., Ltd. All rights reserved.
102 コードをたくさん読むことで 良いコードの審美眼を鍛える -
ソースコードリーディングは心がジーンとする Copyright © Acroquest Technology Co., Ltd. All rights reserved.
103 良いコードを書くには 良いコードを読むことが早道 -
まとめ • ソースコードリーディングはこわくない • ソースコードリーディング力はトレーニングで身に付く • 良いコードを読むことでコードの審美眼を鍛える • Commons DBUtils完全にマスターした
• Javalin完全にマスターした Copyright © Acroquest Technology Co., Ltd. All rights reserved. 104 -
良いソースコードリーディング生活を! Evolve the Earth with Emotion of Technology Copyright ©
Acroquest Technology Co., Ltd. All rights reserved. 105