RE:AutoRegister の簡略化
http://d.hatena.ne.jp/koichik/20051201#1133458230
以前S2Containerリファレンスのmeta説明を読んだときに「これって何に使うんだろう?」と思っていたのですが、こんな使い方があったんですね・・・またまた目から鱗です。
作成していただいたAutoRegisterMetaTagHandlerを早速試してみました。metaタグを2回以上書くとき、2回目のmeta情報の中にpropertyに関する設定があると上手く動かなかったのですが、classPatternContextフィールドをprocessBodyメソッドのローカル変数に変更すると上手く動きました。以前、アノテーションを使ってAspectを登録するようなクラスをゴニョゴニョ作っていたのですが(汗)、あれもこれで動かしたいと思って、AspectAutoRegisterを継承して作り直し、s2container.diconの設定でaddAutoRegisterを呼んで登録してみたところ、アッサリと動作しました。おぉ、なんか凄い・・・
元のタグ記述と比較すると、記述量だけで3分の1ぐらいになりました(驚) タグの記述そのものを簡略化するという考えは全く頭に無かったので、大変勉強になりました。
前回の日記を書いた後、一応自分でも考えてクラスを作ってみたのですが・・・せっかく作ったので、ここで書いておこうかなと思います。ただ、あくまでユーザ視点から、記述簡略化のユーティリティ的なクラスを作っただけなので、一部の環境(J2SE5.0)でしか動きませんが・・・
想定したのは、
- 自動コンポーネント登録の為の命名規約はプロジェクト全体で統一する。
- 自動アスペクト設定登録の為の規約もプロジェクト全体で統一する。
- diconファイルは機能単位で分割し、その単位はパッケージ名で管理する。
・・・というパターンです。分割量が増えた場合、各diconファイルに同じ規約のAutoRegister設定を何度も書くことになるので、コピペを避ける(DRY原則?)為に、プロジェクト全体で1つの規約を定義して、その設定をcomponentに登録し、各diconファイルから使いまわしたい・・・というのが目標です。
で、作ったのが以下のクラス・・・
package mypackage.autoregister; import java.util.ArrayList; import java.util.List; import org.seasar.framework.container.S2Container; import org.seasar.framework.container.annotation.tiger.InitMethod; import org.seasar.framework.container.autoregister.AbstractAutoRegister; import org.seasar.framework.container.autoregister.ClassPattern; public class AutoRegisterInit<A extends AbstractAutoRegister> { protected S2Container container; protected List<A> registerList = new ArrayList<A>(); protected List<ClassPattern> ignoreClassPatterns = new ArrayList<ClassPattern>(); protected String packageName; /** * @param packageName 設定する packageName。 */ public void setPackageName(String packageName) { this.packageName = packageName; } @InitMethod public void registerAll() { for (A register : registerList) { regist(register); } } protected void regist(A register) { register.setContainer(container); if (packageName != null) { for (int i = 0; i < register.getClassPatternSize(); i++) { ClassPattern pattern = register.getClassPattern(i); pattern.setPackageName(packageName); } } for (ClassPattern cp : ignoreClassPatterns) { register.addIgnoreClassPattern(cp); } register.registerAll(); } /** * @param container 設定する container。 */ public void setContainer(S2Container container) { this.container = container; } public void addRegister(A register) { registerList.add(register); } public void addIgnoreClassPattern(String packageName, String shortClassNames) { ignoreClassPatterns.add(new ClassPattern(packageName, shortClassNames)); } }
package mypackage.autoregister; import java.util.ArrayList; import java.util.List; import org.seasar.framework.container.autoregister.ComponentAutoRegister; public class ComponentAutoRegisterInit extends AutoRegisterInit<ComponentAutoRegister> { private List<Class<?>> referenceClasses = new ArrayList<Class<?>>(); public void addReferenceClass(final Class<?> referenceClass) { referenceClasses.add(referenceClass); } /* (非 Javadoc) * @see mypackage.autoregister.AutoRegisterInit#regist(A) */ @Override protected void regist(ComponentAutoRegister register) { for (Class<?> clazz : referenceClasses) { register.addReferenceClass(clazz); } super.regist(register); } }
ComponentAutoRegisterInitは、referenceClassをdiconファイル個別で設定したいときの為に作りました。
・・・で、このInitクラスを使う前提で、プロジェクト共通の規約をauto.diconに定義してみます。
<?xml version="1.0" encoding="Windows-31J"?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "http://www.seasar.org/dtd/components21.dtd"> <components namespace="auto"> <include path="aop.dicon"/> <include path="j2ee.dicon"/> <component name="componentRegister" class="org.seasar.framework.container.autoregister.ComponentAutoRegister" instance="prototype"> <initMethod name="addReferenceClass"> <arg>@mypackage.autoregister.AutoRegisterInit@class</arg> </initMethod> <initMethod name="addClassPattern"> <arg>"mypackage.autoregister"</arg> <arg>".*Impl"</arg> </initMethod> </component> <component name="traceAspectRegister" class="org.seasar.framework.container.autoregister.AspectAutoRegister" instance="prototype"> <property name="interceptor">aop.traceInterceptor</property> <initMethod name="addClassPattern"> <arg>"test"</arg> <arg>".*DaoImpl"</arg> </initMethod> </component> <component name="transactionAspectRegister" class="org.seasar.framework.container.autoregister.AspectAutoRegister" instance="prototype"> <property name="interceptor">j2ee.requiredTx</property> <initMethod name="addClassPattern"> <arg>"test"</arg> <arg>".*ServiceImpl"</arg> </initMethod> </component> </components>
〜Implってクラスを登録し、〜DaoImplってクラスのログを出し、〜ServiceImplってクラスのメソッドにトランザクションをかける・・・という設定にしてみました。S2Containerを変更したり、ClassPatternのパッケージ名を変えたりするので、instance=prototypeで登録してみました。ここでComponentAutoRegisterのregisterAllメソッドが走ってしまうのが問題ですが・・・一応〜Implという名前のクラスが無いパッケージを指定しています。ちょっと微妙ですが・・・
・・・で、この設定を使って、クラス登録用のdiconファイルを作成
<?xml version="1.0" encoding="Windows-31J"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN" "http://www.seasar.org/dtd/components23.dtd"> <components namespace="test"> <include path="auto.dicon"/> <component class="mypackage.autoregister.ComponentAutoRegisterInit"> <property name="packageName">"test.test1"</property> <initMethod>#self.addRegister(auto.componentRegister)</initMethod> </component> <component class="mypackage.autoregister.AutoRegisterInit"> <initMethod>#self.addRegister(auto.traceAspectRegister)</initMethod> <initMethod>#self.addRegister(auto.transactionAspectRegister)</initMethod> </component> </components>
一応これで、パッケージ名以外の記述は全てあらかじめ設定したものを再利用することが出来ます。diconファイルに直接Component設定を追加したい場合は、登録設定とアスペクト設定の間に記述すれば、同じようにアスペクトがかかる・・・筈。記述量としてはそんなに減ってませんが、規約関連の設定を2度書きすることはなくなるので、後はKijimunaのチェック機能にお任せすれば大丈夫かな?
以上が、自分が作った内容なんですが・・・やっぱり、「AutoRegisterを登録するときに一度無意味にregisterAllメソッドを実行してしまう」っていうのが微妙でしょうか・・・フィールドをいろいろ変更しすぎるのも気になりますし・・・