SpringFrameworkで命名規約ベースの設定を実現するArid POJOs

記事のタイトルみたいですが、1行で言えばそういう事です。今まで(Springに)無かったのが不思議なくらいですね。
作者はPOJOs IN ACTIONの著者Chris Richardson氏。
では、早速使ってみます。

基本サンプル

まず下記サイトからダウンロード。
Arid POJOsのWebサイト
Guiceに続きGoogle Codeです。ちなみにオープンソースなホストではCodehausが一番尖ったイメージでかっこ良いと思います。私の中で。どうでも良いですね。

  • 今回試すサンプルのパッケージ構成
    • sample.arid
      • SimpleBootStrap : 起動クラス
      • SimpleClient : サービス呼び出し元
    • sample.arid.service
      • SimpleService : サービスインタフェース
      • SimpleServiceImple : サービス実装

実装の中身は例によってどうでも良いので割愛します(1点だけ、SimpleClientはSetterInjectionです)。
さてXML定義ですが、Arid POJOsの定義はSpring2で導入されたXML Schemaの名前空間拡張を用いています。XML拡張と言えばXBeanはどうしてるだろうと思って1年半ぶりくらいに見たら、なんかよく分からなくなってます…。XBeanは、James Strachan氏がSpringチームに提案したら(熟考の末)拒否られて、「Springチームには失望した!」とか怒ってたんですよね。結局2.0で同じような仕様になったんですが。
話が逸れましたが、Aridの定義。名前空間の宣言と、定義本体。

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:arid="http://chrisrichardson.net/schema/arid"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://chrisrichardson.net/schema/arid http://chrisrichardson.net/schema/arid.xsd">

  <arid:define-beans package="sample.arid" autowire="byType"/>

</beans>

これだけです。パッケージとautowireのタイプを指定しただけ。では実行。

Re: Hello, Arid POJOs!

成功!*1
と、これだけだと何のこっちゃなのですが、上記のXML定義は以下のように書いたのと同じです。

  <bean id="simpleClient" class="sample.arid.SimpleClient" autowire="byType" />
  <bean id="simpleService" class="sample.arid.service.SimpleServiceImpl" autowire="byType"/>

一般化すると、以下のように動作します。

  1. package属性で指定したパッケージ以下(サブパッケージを含む)の全具象クラスを対象に、
  2. クラス名の頭を小文字にし(更に"Impl"で終わる場合は"Impl"を削って)idにし、
  3. autowire属性で指定したautowireを指定して
  4. を定義したものとする。

ちなみにautowireのデフォルト値はconstractorなので、コンストラクタインジェクションの場合にはautowireも省略可能です。

その他の設定

上記の基本設定に加えて、Aridでは以下のような設定が可能です。

指定したパターンに合致するクラスのみを対象とする

パッケージを指定しただけだと、サブパッケージを含む全具象クラスが対象となってしまいます。なんせAridのソースは String classPattern = "classpath*:/" + packagePart + "/**/*.class"; とかなってる。
そこで、pattern属性をつけて、Spring2.0らしいAspectJ文法で更にクラスを絞り込めます*2。

  <arid:define-beans package="sample.arid"
  pattern="@org.springframework.transaction.annotation.Transactional sample..Simple*"
  autowire="byType"/>

この例だと、@Transactionalが付いていて、sampleパッケージ以下で、クラス名がSimpleで始まるクラスを対象としています。

クラス名⇒bean名の変換ルールを変える

デフォルトだと頭を小文字化&末尾にImplがあったら除去、というルールですが、自前で変換ルールを実装して使用する事ができます。
実装例は省略しますが、AridBeanNameGeneratorを実装して、XMLで以下のようにname-generator属性を指定するだけです。

  <arid:define-beans package="sample.arid" name-generator="sample.arid.MyBeanNameGenerator" />
一部のクラスを例外的な定義にする

以下のように記述する事で、自動的に生成される定義をオーバーライドする事が可能です。

  <arid:define-beans package="sample.arid" autowire="byType">
    <arid:extras>
      <bean id="simpleService" class="sample.arid.service.SimpleServiceImpl">
        <property name="key" value="1" />
      </bean>
    </arid:extras>
  </arid:define-beans>

この例では、"simpleService"というidの定義をオーバーライドしていますので、実行時は自動的に作られる定義ではなくこちらの定義が採用されます。

結論

タイトルにも書いた通り、このArid POJOsを使えばSpring Frameworkで所謂CoC(Convention Over Configuration)を部分的に実現できますので、XMLが肥大化する場合のソリューションとして検討する価値があると思います。もちろん省略する事によるデメリットもありますが、マクロ展開に近いようなシンプルな仕組みなので部分的導入も可能ですし、なにより「省略する」という選択肢ができた事がとても喜ばしいですね。

*1:ソース見せずに「成功!」もないですが

*2:AspectJ weaverが必要なので、Springのdependencyに入ってるやつを使うなどして下さい。mavenを使うと勝手に入ると思います。