このページに書いてあること
Springを使う時に(というか全てのフレームワークが大抵当てはまりますが)重要になる考え方を説明します。
使い方とかではなくて、Frameworkの概念そのものといってもいいかもしれません。
これを知っているのと知らないのでは、Springが動かないなんていうときの調査の効率が変わってきます。
ハリウッドの原則ってなんだというところと、Spring Frameworkにどうか関わっているのかを見てみましょう。
このブログ内でもいくつかSpringについての記事があるので、よければ参考にしてみてください。
Frameworkってなんだっけ問題
初心者がまず最初に手にする本にはざっくり説明が乗っていますが、その説明で多くの比重が割かれているのは大抵の場合アノテーションの使い方とか、DBとのアクセスの仕方といった内容がほとんどです。
Frameworkってそもそもなんだっけという話については大抵の場合触れられていません。
あったとしても、起動時のライフサイクルがどうなっているとかそういう話だけですし、大抵のプログラマはその辺を読まないことが多いのではないでしょうか。
知りたいのはどうやってリクエストバリデーションを掛けられるのかという点ですから。
ただ、それで使ってると後の調査で詰まる可能性が高いです。
ありがちなFrameworkの理解
大抵の初心者の方は、Frameworkを便利ライブラリの集まり程度に思っているかもしれません。
色々なライブラリがデフォルトで入っており、例えばBase64エンコードをかけたり、オブジェクト同士を簡単に比較したり、アノテーションつけるだけで何か動いてくれたり。
特にSpring Frameworkに関して言えば、@Controllerで@RequestMappingをつけておけばリクエストの受信ができて便利!くらいに思っている人がウチの若手には多いです。
ハリウッドの原則って何だ問題
ハリウッドの原則という言葉を知っていますか?詳細はWikipediaにあるので詳しくは書かないですが、これは制御反転の原則を表したものになっています。
で、Frameworkというのはハリウッドの原則を前提に作られています。
簡単に書くと、「こっち(Framework)が呼ぶまで、君たち(自分で実装したクラス)は何もするな」ということでしょうか。
で、これとFrameworkがどう関係してるの?というのを簡単にでいいので理解してほしいというのがテーマです。
大抵の教本では原則がちらっと書かれているだけで、それとの関係性が不明なままアノテーションの説明にうつり、いざバグった時に検討外れの調べ方をしてしまいます。
上図の理解でプログラムを書いている人だと、例えばFrameworkの軌道でこけたときに慌てふためいてしまうこととなります。
ここで重要なのはFrameworkと自分で書いたコードを別のものであると考えてはいけないということです。
このFrameworkにおけるリクエスト受付機能の部分がハリウッドの原則そのものとなっています。Framework側がリクエスト受信に関わる機能を提供してくれているおかげで、プログラマはリクエストのマッピング設定やPOST/GETなどの割り付けを実装する必要がありません。ではどうするのかというと、リクエスト固有の処理は@Controllerを付与したクラスで処理を定義します。これがControllerクラスの役割です。
つまり、@Controllerというのは、「SpringFrameworkにリクエスト処理がきたときに処理をこのクラスに投げてね」という依頼になります。また、@RequestMappingを使用した場合は、「特にこのURLできた時にこのメソッドを呼んでね」ということをFramework側に通知しているわけです。そしてController側はSpring Frameworkから呼び出されるのを待っているわけです。この関係がハリウッドの原則と言われる部分です。
また、@Serviceや@Componentなどのアノテーションをつけたときにどのようにクラスの実態がコールされるのかという点もFrameworkが秘匿してくれるようになります。(ただし、どのクラスを呼ぶかのコードは実装がわで決めてやる必要があります。とはいっても、アノテーションによりポリフォーリズムを確保することができます。)
これらはSpring内部にBeanという形で登録されており、各ServiceやRepositoryクラスの型がズラッと格納されています。そして、Controllerクラスから呼び出されたタイミングでBeanからインスタンスを生成してServiceなどの処理を実行します。なので、ここでもServiceは呼び出されるのを待っているということになります。
Frameworkで問題が起きた時の勘所
Frameworkは上記のような動きでハリウッドの原則を実現していました。
Frameworkでは色々と仕組みが提供されていて、必要となる部分(実際の業務処理やDBアクセス部分)をプログラマが実装しているという形になります。
つまり、 Frameworkを使ったプログラムはあくまでも Frameworkのルールに則って書いていかないといけません。
例えば、「Beanが登録されていません」とか「Bean名が重複しています」とか、「Repositoryクラスがバインドされていません」みたいなエラーが出た時は、自分の書いたコードそのものを見るよりも、 Frameworkへの設定部分(=アノテーション)を確認してやるのが優先事項となります。
これらの考えを持たずにデバッグした結果、一生懸命クラスの構成を変えてみたり、メソッドの処理を直してみたりと見当外れのことをして時間を浪費するプログラマが多くいます。
自分の書いたコードのうち、処理本体に問題があるのか、それとも Frameworkへの設定(=アノテーション)の仕方に問題があるのかといのはしっかり切り分けてほしいと思います!
実際に遭遇した具体例
Spring MVCでコーティングをしているときに、Repositoryクラスを使用しようとしていました。ライブラリはSpring Data JPAです。
通常、Repositoryクラスを使うときは@Repositoryアノテーションを付与して、Entityクラスを型指定してやれば、主キーでの検索などを実施してくれます。
そして、SpringMVCで@Repositoryアノテーションを付与したクラスを作ってSpringを起動すると・・・「RepositoryクラスがBeanに定義されていません」エラー。
担当の作業者は、自分の書いたServiceクラスからのRepositoryクラス呼び出し部分とか、Repositoryクラスの型引数を変更したりと、「自分の書いたコード」を一生懸命変更していました。
しかし、今使おうとしているのはSpring-Data-JPAです。通常のSpringとはまた違うお作法があります。
自分の使おうとするライブラリのお作法をしっかりと調べて、理解した上で使わないとこのようなことになってしまいます。(内部を理解する必要はありません。使い方を理解しないといけません。)
先のように、何となくでFrameworkを使っていると、トラブル時に自分のコードばかりを調べて、Frameworkへの設定を確認するという意識そのものが湧いてきません。
今回の場合は、通常のconponent-scanの他に、Repositoryクラスはここだよという指定を別途記述してやる必要がありました。
これはSpring-data-JPAという Frameworkのルールです。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- JPA Repository -->
<jpa:repositories base-package="jp.co.repository" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
こういった形で、Framework本体の問題なのか、自分の実装の問題なのかというのをしっかりと切り分けながら調査を行いましょう!
Frameworkが起動しないとかっていうのは大抵Frameworkへの設定の問題ですけどね・・・アノテーションの変更や、Spring用の設定xmlへの記述で動く場合がほとんどです。
たまーに、パッケージ違いで同じクラス名があった時とかに@Componentのサービス名が被ってたりということもありますね。