xbean-springでSpringFrameworkのbeanを JNDIで公開する


フル J2EE コンテナを使わずに SpringFramework だけ利用する場合って、
JNDI を前提にされると結構めんどくさいことがありますよね。


たとえば、UserTransaction を JNDI 経由で取得するのが前提の場合とか。


そんなとき、xbean-spring の SpringInitialContextFactory が便利です。


xbean-spring を使うと、Spring の bean 定義ファイルを元に BeanFactory
を生成し、任意の bean を JNDI 経由で公開することができます。

XBean project

XBean project は、Apache Geronimoのサブプロジェクトらしいです。
http://geronimo.apache.org/xbean/


おいらは mavenリポジトリから xbean-spring の jar を落としてるだけなので、
あまり詳しいことは知りません (^^;

ユーザーのアプリケーションは Spring Framework に依存しません

なんとなく、アプリが Spring に依存するのではという雰囲気をかもし出していますが、
そんなことはありません。


JNDI で Context を公開する仕組みとして Spring が使われているだけなのです。


あくまでも公開側だけです。


ご安心を。


J2EE コンテナが提供する JNDI をルックアップする処理のテストを書くときとか、
いいかもしれない。

SpringInitialContextFactory

さて、いきなり核心に迫ります。


核心は、org.apache.xbean.spring.jndi.SpringInitialContextFactory クラスです。


SpringInitialContextFactory は、InitialContextFactory の実装です。そのため、通常の JNDI の初期化手順を踏むだけで、SpringFramework の bean を JNDI で公開することができます。

こんな例を考えてみた

例。
  • InitialContext を new して、
  • "key1" というキーに対して "value1" という String を返し、
  • "key2" というキーに対して、BeanFactory で生成された bean を返す。

見るからに適当な例ですねw

ソース的にはこんな感じ。
Context ctx = new InitialContext();
System.out.println("ctx.lookup(key1) = " + ctx.lookup("key1"));
System.out.println("ctx.lookup(key2) = " + ctx.lookup("key2"));
こんな出力を予定。
ctx.lookup(key1) = value1
ctx.lookup(key2) = java.lang.Object@8fce95
jarの取得

maven なら、こんな感じ。

<dependency>
    <groupId>org.apache.xbean</groupId>
    <artifactId>xbean-spring</artifactId>
    <version>3.4.1</version>
</dependency>

最新版はここらへんからチェック

ということで、この(やる気のなさそうな)例を実現するソースに続く。

ソース

jndi.properties


JNDI関連のプロパティを設定するファイルです。


とりあえずクラスパスに通しておけばOK。(他にもいろいろな方法があるけど)

java.naming.factory.initial=org.apache.xbean.spring.jndi.SpringInitialContextFactory
java.naming.provider.url=jndi.xml
jndi.xml


JNDI で公開する Context を構成するための情報を提供する Spring の定義ファイルです。

<?xml version="1.0" encoding="UTF-8"?>
<beans
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"
>

  <bean id="value2" class="java.lang.Object" />

  <bean id="jndi" 
        class="org.apache.xbean.spring.jndi.SpringInitialContextFactory" 
        factory-method="makeInitialContext">
    <property name="entries">

      <!-- Entries of the follwing map are to be exported through JNDI -->
      <map>

        <!-- String value -->
        <entry key="key1" value="value1"/>
        
        <!-- bean -->
        <entry key="key2" value-ref="value2" />

      </map>

    </property>
  </bean>

</beans>
Example.java


JNDI のルックアップを行うメインのアプリケーションです。

import javax.naming.Context;
import javax.naming.InitialContext;

/**
 * Sample for xbean-spring
 * 
 * @author beyondseeker@users.sourceforge.jp
 * @version $Id$
 */
public class Example {
    
    /**
     * Sample for xbean-spring
     * 
     * @param args
     *            N/A
     * @throws Exception
     *             N/A
     */
    public static void main(String[] args) throws Exception {
        Context ctx = new InitialContext();
        System.out.println("ctx.lookup(key1) = " + ctx.lookup("key1"));
        System.out.println("ctx.lookup(key2) = " + ctx.lookup("key2"));
    }
    
}
出力結果
ctx.lookup(key1) = value1
ctx.lookup(key2) = java.lang.Object@8fce95


文字列と任意の bean が公開できました。


ラクチンですね (`・ω・´)

初期化の流れ

Contextの取得方法


普通に、new InitialContext() とかすると、以下のような感じで、コンストラクタ内でいろいろとごにょごにょやってくれます。

InitialContextFactory 実装の特定


InitialContextFactory 実装は、システムプロパティや jndi.properties の java.naming.factory.initial プロパティにて指定されたクラス名のものが採用されます。


ここらへんのルールは、xbean-spring の独自ルールではなく、jndi 共通のルールなので、 javax.naming.Context あたりの javadoc を漁ると情報が転がっています。


SpringInitialContextFactory を使用する場合は、当然ながら、以下のように設定を行います。

java.naming.factory.initial=org.apache.xbean.spring.jndi.SpringInitialContextFactory


※ちなみに、java.naming.factory.initial という文字列は、javax.naming.Context インタフェースの中で、以下のように定義されています。

String INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial";
Context の生成開始


javax.naming.spi.NamingManager から SpringInitialContextFactory#getInitialContext(Hashtable) が呼び出され、シングルトンの Context の生成が開始されます。

SpringFramework の bean 定義ファイルの特定


システムプロパティや jndi.properties の java.naming.provider.url プロパティで指定された文字列からリソースを取得します。文字列は、Springのリソース文字列 (classpath://foo.xml や file://foo/bar.xml や URL) として扱われます。


デフォルトでは、jndi.xml という名前が使用されます。


※ちなみに、java.naming.provider.url という文字列は、javax.naming.Context インタフェースの中で、以下のように定義されています。

String PROVIDER_URL = "java.naming.provider.url";
BeanFactory の生成


上記で特定された SpringFramework の bean 定義ファイルを用いて、BeanFactory が生成されます。

Context の生成


上記で生成された BeanFactory から、"jndi" という id の bean を取得します。(SpringInitialContextFactory クラスの makeInitialContext メソッドをファクトリメソッドとして使用)


※ちなみに、bean の "jndi" という id は、ソース内にべったりと書かれているので、変更できません (´・ω・`)


生成された bean は org.apache.xbean.spring.jndi.DefaultContext クラスのインスタンスで、jndi bean の定義に書かれているプロパティ entries が DI されて返されます。そして、その内容が、Context の内容となります。


簡単ですね (`・ω・´)