S2Container の初期化

 前回までで Seasar ではどのようにコンポーネントが生成されるのかが分かりました。まず、今回 Seasar のエントリ・ポイントは SingletonS2ContainerFactory#init() でした。ここで dicon ファイルの解析が行われて S2Container が生成され、 ComponentDef が S2Container に登録されるのでした。でも、恐らくコンポーネントは最初にコンテナを作った時点でコンテナに配置される筈です。SingletonS2ContainerFactory#init() を見てみましょう。

/**
 * 設定ファイルに基づいてS2コンテナを生成・初期化し、それを保持します。 既にS2コンテナが保持されている場合は何もしません。
 * <p>
 * S2コンテナを生成した後、 初期化を行なう前に必要に応じて、
 * 外部コンテキストおよび外部コンテキストコンポーネント定義レジスタをS2コンテナに設定します。
 * </p>
 * 
 * @see S2ContainerFactory#create(String)
 * @see org.seasar.framework.container.ExternalContext
 * @see org.seasar.framework.container.ExternalContextComponentDefRegister
 */
public static void init() {
    container = S2ContainerFactory.create(configPath);
    if (container.getExternalContext() == null) {
        if (externalContext != null) {
            container.setExternalContext(externalContext);
        }
    } 
    container.init();
}

 良く見ると S2Container#init() が呼び出されています。ここではクラスローダの設定や ComponentDef#init() が呼び出されています。クラスローダの設定については、Seasarホットデプロイの影響などでややこしくなっていると思われるの省略します(分かる自信も無い・・・)。ComponentDef#init() では、実装クラスの取得および ComponentDeployer の準備が行われます。ComponentDefImpl#init() を見てみましょう。

public void init() {
    getConcreteClass();
    getComponentDeployer().init();
}

 getConcreteClass() では、アスペクトの適用を行って具象クラスを設定しています。これにより Seasar で管理されるコンポーネントは全てアスペクトの対象になるわけですね。まさにここ、AopProxy が Seasar におけるアスペクトを織り込むポイントになっているようです。この点については後ほど暇があったら見てみたいなーと思います。

public Class getConcreteClass() {
    concreteClass = AopProxyUtil.getConcreteClass(this);
    return concreteClass;
}
/**
 * 完全なクラスを返します。
 * 
 * @param componentDef
 * @return 完全なクラス
 */
public static Class getConcreteClass(final ComponentDef componentDef) {
    final Map parameters = new HashMap();
    parameters.put(ContainerConstants.COMPONENT_DEF_NAME, componentDef);
    AopProxy proxy = new AopProxy(componentDef.getComponentClass(),
            getAspects(componentDef), getInterTypes(componentDef),
            parameters);
    return proxy.getEnhancedClass();
}

 次に ComponentDeployer#init() が実行されており、Seasar デフォルト設定の(つまり、コンテナの管理するインスタンスはシングルトンとして扱う)場合には、この段階でコンポーネントのデプロイが行われます。つまり、SingletonS2ContainerFactory#init() が実行されると、コンポーネントがコンテナにデプロイされているわけですねー。

コンポーネントの生成

 さて無事に生成された S2Container ですが、次はどのように S2Container に保持された ComponentDef からオブジェクトが生成されるのかを見てみたいと思います。
 まず、S2Container に登録された ComponentDef は、抽象化されたインターフェース S2ContainerBehavior#Provider から取得されます(大抵の場合に於いては)。S2ContainerImpl#getComponent() で、S2Container は S2ContainerBehavior#Provider から取得した ComponentDef を手がかりに、 ComponentDef#getComponent() を呼び出してインスタンスを取得します。ComponentDefImpl#getComponent() は以下のように実装されています。

public Object getComponent() {
    return getComponentDeployer().deploy();
}

 インターフェース ComponentDeployer が利用されています。ここで ComponentDef に組み込まれた種々の情報を組み立ててインスタンスを生成するようです。

/**
 * インスタンス定義に応じてインスタンス生成や外部コンテキストへの配備などを行った後に、 そのコンポーネントのインスタンスを返します。
 * 
 * @return コンポーネントのインスタンス
 * 
 * @see org.seasar.framework.container.deployer.SingletonComponentDeployer#deploy()
 * @see org.seasar.framework.container.deployer.PrototypeComponentDeployer#deploy()
 * @see org.seasar.framework.container.deployer.ApplicationComponentDeployer#deploy()
 * @see org.seasar.framework.container.deployer.RequestComponentDeployer#deploy()
 * @see org.seasar.framework.container.deployer.SessionComponentDeployer#deploy()
 */
public Object deploy();

 段々コピペが多くなってきましたが、気にせず続けます。SingletonComponentDeployer では以下のようにインスタンスを組み立てています。循環参照などもここで検知されるようです。

private void assemble() {
    if (instantiating) {
        throw new CyclicReferenceRuntimeException(getComponentDef()
                .getComponentClass());
    }
    instantiating = true;
    try {
        component = getConstructorAssembler().assemble();
    } finally {
        instantiating = false;
    }
    getPropertyAssembler().assemble(component);
    getInitMethodAssembler().assemble(component);
}

 ConstructorAssembler が ComponentDef を元に組み立てています。AbstractConstructorAssembler を見てみましょう。引数が存在する場合には ArgDef から引数を取得しているのが分かります。

/**
 * コンポーネント定義に基づいてコンポーネントを組み立てます。
 * 
 * @return コンポーネント
 */
protected Object assembleManual() {
    Object[] args = new Object[getComponentDef().getArgDefSize()];
    for (int i = 0; i < args.length; ++i) {
        try {
            args[i] = getComponentDef().getArgDef(i).getValue();
        } catch (ComponentNotFoundRuntimeException cause) {
            throw new IllegalConstructorRuntimeException(getComponentDef()
                    .getComponentClass(), cause);
        }
    }
    BeanDesc beanDesc = BeanDescFactory.getBeanDesc(getComponentDef()
            .getConcreteClass());
    return beanDesc.newInstance(args);
}

/**
 * デフォルトのコンストラクタを使ってコンポーネントを組み立てます。
 * 
 * @return コンポーネント
 */
protected Object assembleDefault() {
    Class clazz = getComponentDef().getConcreteClass();
    Constructor constructor = ClassUtil.getConstructor(clazz, null);
    return ConstructorUtil.newInstance(constructor, null);
}

 また、PropertyAssembler や InitMethodAssembler によって、生成されたコンポーネントに対するプロパティ・インジェクションおよびメソッド・インジェクションが実行されています。なんだかインジェクションという言葉が多くて混乱してしまいますが、property や initMethod は dicon ファイルの Component タグで設定可能なコンポーネント生成時の振る舞いを設定するものです。普段あまり使う機会は無さそうです。詳しくは Seasarリファレンスガイドを参照しましょう。
 これで Seasar がどのようにコンポーネントを生成しているのか分かりました。ちなみにここで ArgDef を使っていますが、ここでの ArgDef は dicon ファイルの arg タグで指定した引数です。・・・あれあれ、Seasar でインジェクションされたコンポーネントには、引数に指定されたインターフェースの実装を勝手に組み込んでくれるんだけど、それはどのように実現しているんだろう?
 
 S2Container が勝手にコンポーネントを注入してくれる機能は「自動バインディング」と呼ぶそうです。早速調べてみると、あったあった AutoBindingDef というインターフェースが存在しますね。ここに2つのメソッドが定義されています。

/**
 * 自動バインディング定義に基づき、 <code>componentDef</code>に対する{@link ConstructorAssembler}を返します。
 * 
 * @param componentDef
 *            コンポーネント定義
 * @return 自動バインディングの範囲が設定された{@link ConstructorAssembler}
 */
ConstructorAssembler createConstructorAssembler(ComponentDef componentDef);

/**
 * 自動バインディング定義に基づき、 <code>componentDef</code>に対する{@link PropertyAssembler}を返します。
 * 
 * @param componentDef
 *            コンポーネント定義
 * @return 自動バインディングの範囲が設定された{@link PropertyAssembler}
 */
PropertyAssembler createPropertyAssembler(ComponentDef componentDef);

 先ほど SingletonComponentDeployer では、getConstructAssembler() により抽象クラス AbstractComponentDeployer からアセンブラを取得していました。よく見るとここで AutoBindingDef からアセンブラをセットアップしています。自動バインディングを行う場合、AutoConstructorAssembler が使用されるようです。では、AutoConstructorAssembler で実際にどのようにインスタンスが生成されるか見てみます。

protected Object doAssemble() {
    Constructor constructor = getSuitableConstructor();
    if (constructor == null) {
        return assembleDefault();
    }
    Object[] args = getArgs(constructor.getParameterTypes());
    return ConstructorUtil.newInstance(constructor, args);
}

/**
 * 引数を返します。
 * 
 * @param argTypes
 * @return 引数
 */
protected Object[] getArgs(Class[] argTypes) {
    Object[] args = new Object[argTypes.length];
    for (int i = 0; i < argTypes.length; ++i) {
        try {
            args[i] = getComponentDef().getContainer().getComponent(
                    argTypes[i]);
        } catch (ComponentNotFoundRuntimeException ex) {
            // logging ...
            args[i] = null;
        }
    }
    return args;
}

 おぉ、出てきました。ComponentDef から S2Container を取得し、更に getComponent(argTypes[i]) していますね。こうして同じコンテナに登録されているコンポーネントを引数に登録するようです。
 一通り謎も解けたので、次回は再度 S2Container の生成部分を見直してみます。

Container の組み立て

 難しいところは省略していくよー。 S2ContainerFactory#createからProvider#build を呼び出す。お、S2ContainerBuilder という如何にもな名前のインターフェースを発見。SingletonS2ContainerFactory#init で、設定した configPath の dicon ファイルを解析します。実態は XmlS2ContainerBuilder で、 TagHandlerContext にパースした dicon のコンテキスト情報(つまり, S2Container!)を保持します。AbstractTagHandler が SAX で解析するときのハンドラで、複数のタグに対応しています。例えば以下のクラスが存在します。例えばアスペクトってどうやって織り込まれてるの?って確認したいときは, AspectTagHandler から読み進めると良いんじゃないかと思います。アスペクトに関しては後日読もうかと思っています。

  • ArgTagHandler
  • AspectTagHandler
  • ComponentsTagHandler
  • ComponentTagHandler
  • IncludeTagHandler
  • InterTypeTagHandler
  • MetaTagHandler
  • MethodTagHandler
  • PropertyTagHandler

 Seasar の dicon ファイルでは Components がルートタグになっている(と思う・・・調べてない)ので, まず ComponentsTagHandler で Container を生成します。実際のコンテナの実装クラスは書いていませんが、ここが Container の生まれる場所ですね。勿論, 親のコンテナがあれば親もセットされます。

protected S2Container createContainer() {
    return (S2Container) ClassUtil.newInstance(containerImplClass);
}

 次に, ComponentTagHandler で順次コンポーネント定義をコンテキストに push していきます。今回は面倒なので ComponentTagHandler のみ見てみます。取り敢えず大事そうなところだけ抽出してみましょう。タグの開始に対するコールバックとして, コンテキストに ComponentDef を push します。

public void start(TagHandlerContext context, Attributes attributes) {
    String className = attributes.getValue("class");
    Class componentClass = null;
    if (className != null) {
        componentClass = ClassUtil.forName(className);
    }
    String name = attributes.getValue("name");
    ComponentDef componentDef = createComponentDef(componentClass, name);
    String instanceMode = attributes.getValue("instance");
    if (instanceMode != null) {
        componentDef.setInstanceDef(InstanceDefFactory
                .getInstanceDef(instanceMode));
    }
    String autoBindingName = attributes.getValue("autoBinding");
    if (autoBindingName != null) {
        componentDef.setAutoBindingDef(AutoBindingDefFactory
                .getAutoBindingDef(autoBindingName));
    }
    context.push(componentDef);
}

 タグが終了したら, コンポーネント定義を S2Container に登録します。ここでようやく S2Container#register が登場しました。式の解析なども行われているようですが、特に興味も無いので飛ばします。

public void end(TagHandlerContext context, String body) {
    ComponentDef componentDef = (ComponentDef) context.pop();
    String expression = null;
    if (body != null) {
        expression = body.trim();
        if (!StringUtil.isEmpty(expression)) {
            componentDef
                    .setExpression(createExpression(context, expression));
        } else {
            expression = null;
        }
    }
    if (context.peek() instanceof S2Container) {
        S2Container container = (S2Container) context.peek();
        container.register(componentDef);
    } else {
        ArgDef argDef = (ArgDef) context.peek();
        argDef.setChildComponentDef(componentDef);
    }
}

 S2Container で登録された ComponentDef は, S2ContainerImpl#componentDefMap に保持されます。次はここからどのように ComponentDef を読み込み、クラスを生成してインジェクションされるか見てみましょう。

SingletonS2ContainerFactory~S2Container

 取り敢えず "S2Container 2.4.35" をダウンロードしてみました。エントリポイントは SingletonS2ContainerFactory だと思い込んで読む。
うん、何か S2Container を生成してるだけ。Singletonの意味は、S2ContainerがSingletonって意味ですね。SingletonじゃないS2ContainerFactoryもあるんだろうなーと思ったらやっぱりあった。こちらは同期化されているけど、Singletonの方はスレッドアンセーフなんだなー。SingletonだからS2Containerはアプリケーション起動時にしか生成されないってことですかね。

 S2Containerは名前の通りS2の本丸。S2ContainerImplが実装クラス。ContainerConstantsという定数定義されたインターフェースを実装しているのが気持ち悪いー。何で定数定義がインターフェースになってるんだ・・・。
取り敢えずインポートが完了したので、今後はどうやってコンポーネントがインジェクションされるのか読む。

今後の計画

 気がつけば随分休んでしまいました。夏といえば海!海といえばサーフィン!の季節です。
 少し勉強計画を修正しようと思います。まず、英語は当面優先度を下げます。ちょこちょこ単語を覚えていこうかなと。平日の朝はソースコードリーディングをしようと思います。当面は Seasar を読みます。S2Container, S2DBCP辺りかな。全然使ったことないのでどう読んでいくか悩みますが・・・。

第二十三章: 前置詞, 第二十四章: 接続詞

一気にForestも終了です!
こちらは内容多過ぎなのでまとめはしません。後で復習だけしときます。

従属を表す "in"

"in"を使って仕事に従事していることを表すことが出来る。

  • He is in publishing. (彼は出版関係の仕事をしている)

時を表す "at/on/in"

  • at: 時刻
  • on: 日付/曜日
  • in: 月/年

期間を表す "for/during/in"

  • for: 期間の長さを表すときに使う。
  • during: 「夏の間」など、どういう期間かを表すときに使う
  • in: 何かをするときに、どれぐらいの時間がかかるのかを表すときに使う

aboveとbelow

  • above: 〜より高いところに
  • below: 〜より低いところに

否定文で使われる "or"

否定文で"or"が使われると、「どちらも〜ない」という意味になることに注意。

  • The road was not very wide or easy to find. (その道はそれほど広くもなく、見つけ易くもなかった)

第二十二章: 副詞

他の品詞に分類出来ないものをまとめて副詞と呼んでいる。

副詞の位置

<動詞+目的語>の直後に置くことが多い。

  • She took my advice seriously.

助動詞があるときは助動詞と動詞の間に置く。

  • You should carefully look at the broken statue.

副詞の前に前置詞は置けないので、例えば以下の文は×。

  • I want to go to abroad.

"abroad"は副詞なので、

  • I want to go abroad.

が正しい。

形によって意味の違う副詞

  • late(遅く)<-> lately(最近)
  • most(最も)<-> mostly(大抵は)
  • near(近くに)<-> nearly(ほぼ)

agoとbefore

agoは現在を基準にして「今より〜前」を表す。
beforeは過去のある時点を基準にして「その時点より〜前」を表す。

tooとeither

どちらも「〜もまた」という意味だが、tooは肯定文、eitherは否定文で用いる。

  • "I can't eat raw fish." "I can't, either."