DI/AOPコンテナで有名どころと言えば「Spring」ですよね。
純国産のDI/AOPコンテナに、「Seasar2」もありますね。
私も、普段はSpringを使っているんですが、ところどころ思うことがあります。
「Springイケてない、Seasar2の方が気が利いてるなぁ」と。
その1つとして、XML定義において「初期化時に自由にメソッドが呼べないこと」があります。
「いやいや、Springでも、beanタグにinit-method属性があるじゃないか」とおっしゃる人もいると思いますが、これだとメソッドに引数を渡せないのが、時と場合によっては非常に不便なんです。
あと、メソッドを1個しか呼べないのも。
Seasar2ならinitMethod「タグ」があり、引数も呼び出す回数も思いのまま。
ということで、Springの気が利かない、この初期化メソッド呼び出しを、
Seasar2のように、引数を渡したり複数回呼び出したりできるように改良をチャレンジしてみます。
最終的には、こんな風に書けるようになることがゴール。
<bean id="table" class="sample.Table"> <init-method>self.addColumn("userId", "ユーザID")</init-method> <init-method>self.addColumn("userName", "ユーザ名")</init-method> </bean>
その前に、まずはSpringで独自のタグを作成する方法について、メモしておきます。
(いろいろ探してみたけど、英語のサイトしかなかった・・・)
独自定義のタグを作ってみる
一旦本題から離れて、独自定義のタグを作る方法について書きます。
SpringのBean定義で以下のようなオブジェクト構造を作る場合、
普通に書くと、
<bean id="table" class="sample.Table"> <property name="columns"> <list> <bean class="sample.Column"> <property name="id" value="userId" /> <property name="name" value="ユーザID" /> </bean> <bean class="sample.Column"> <property name="id" value="userName" /> <property name="name" value="ユーザ名" /> </bean> </list> </property> </bean>
となりますが、この
今回作るファイルは、以下の5つ。
「〜Parser.java」というファイルは独自のタグを作成する分だけ作成します。
XSDファイルの作成
まずは、タグの構造を定義するXSDファイルを作成します。
sample.xsd
<?xml version="1.0" encoding="UTF-8" standalone="no" ?> <xsd:schema xmlns="http://www.foo.com/schema/sample" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.foo.com/schema/sample"> <xsd:element name="column"> <xsd:complexType> <xsd:attribute name="id" type="xsd:string" use="required" /> <xsd:attribute name="name" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:schema>
これにより、columnタグの定義ができました。
ハンドラとパーサの作成
次に、XMLを解析してオブジェクトを生成するハンドラとパーサを作成します。
ここでは、columnタグを解析するパーサを登録します。
SampleBeanXmlHandler.java
public class SampleBeanXmlHandler extends NamespaceHandlerSupport { public void init() { // columnタグを解析するオブジェクトを登録する registerBeanDefinitionParser("column", new ColumnBeanDefinitionParser()); } }
ColumnBeanDefinitionParser.java
public class ColumnBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { protected Class<?> getBeanClass(Element element) { // Columnクラスのオブジェクトを生成することをSpringに伝える return Column.class; } protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { // XMLのcolumnタグのid属性の値を取得する String id = element.getAttribute("id"); if (StringUtils.hasLength(id)) { // 属性が指定されている場合は、Columnのidプロパティに値をセットする builder.addPropertyValue("id", id); } String name = element.getAttribute("name"); if (StringUtils.hasLength(name)) { builder.addPropertyValue("name", name); } } }
namespaceとXSDファイルの関連付け
src/main/resources/META-INF配下にspring.schemasファイルを作成して、XSDのURLとXSDファイルの関連付けを行います。
http\://www.foo.com/schema/sample/sample.xsd=sample/xml/sample.xsd
URLがhttpから始まっていますが、あくまでこれは名前であり、
実際に上記URLにファイルを配置しなくても動作します。
(Spring等では、実際にインターネット上にXSDファイルを配置しています。)
namespaceとハンドラの関連付け
spring.schemasと同じく、src/main/resources/META-INF配下にspring.handlersファイルを作成して、namespaceとハンドラの関連付けを行います。
http\://www.foo.com/schema/sample=sample.xml.SampleBeanXmlHandler
作成したタグの使用
設定は完了したので、最後はタグを使用する方法です。
beansタグのxmlns属性で、作成したタグのnamespaceを定義します。
あとは、<namespace:タグ名>の形式で記述するだけ。
context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sample="http://www.foo.com/schema/sample" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.foo.com/schema/sample http://www.foo.com/schema/sample/sample.xsd"> <bean id="table" class="sample.Table"> <list> <sample:column id="userId" name="ユーザID" /> <sample:column id="userName" name="ユーザ名" /> </list> </bean> </beans>
これで、独自定義のタグ「column」が使用できるようになりました。