Di xml の Expression

Nashorn JavaScript

Di xml 上のプロパティはコンポーネントの expression のパーサーとして、Javaに組み込まれている、JavaScriptエンジン Nashorn を使っています。

これにより、依存ライブラリなしで Di xml を利用することができます。

一方で、OGNL や Groovy などに比べて若干表現力は落ちる可能性があります。 ですが、Di xml での expression は、ちょっとした生成ロジックの補佐に徹して、あまり複雑になり過ぎないようにする方がよいと考えています。 例えば、LastaFluteであれば、環境依存のコンフィグのようなものは、_env.properties で管理することをオススメしています。

ですが、Di xml でのニーズを完全に把握し切れていませんので、ユーザーの方々のフィードバックや利用状況を考慮しながら、バランスよく対応していきたいと思います。

e.g. Di xml vaious pattern  @Dixml
<components>
    <include path="lastaflute.xml"/>
    <include condition="#exists('#path')" path="trial_option.xml"/>
    <include condition="#SMART == 'alwaysNotMatch'" path="rdb.xml"/>

    <component name="sea" class="org.docksidestage.mylasta.trial.TrialDiSea"/>
    <component name="land" class="org.docksidestage.mylasta.trial.TrialDiLand"/>
    <component name="parks">[sea, land]</component>
    <component name="maihama" class="org.docksidestage.mylasta.trial.TrialDiMaihama">
        <property name="parkList">parks</property>
    </component>

    <component name="expSea" class="org.docksidestage.mylasta.trial.TrialExpSea">
        <property name="simpleString">"dockside"</property>
        <property name="simpleInteger">3</property>
        <postConstruct name="addStringList">
            <arg>["a","b"]</arg>
        </postConstruct>
        <postConstruct name="addStringMap">
            <arg>{"sea.over": "land.oneman"}</arg>
        </postConstruct>
        <postConstruct name="addIntArray">
            <arg>(int[])[1,2]</arg>
        </postConstruct>
        <postConstruct name="addStringArray">
            <arg>(String[])["sea" ,  "land" ]</arg>
        </postConstruct>
        <postConstruct name="addType">
            <arg>@org.dbflute.Entity@class</arg>
        </postConstruct>
        <postConstruct name="addStaticFinal">
            <arg>@jp.sea.SeaPark@STATIC_DEF</arg>
        </postConstruct>
        <postConstruct name="addStaticMethod">
            <arg>@jp.sea.SeaPark@callStatic()</arg>
        </postConstruct>
        
        <postConstruct name="addConfigProp">
            <arg>provider.config().getJdbcUrl()</arg>
        </postConstruct>
        <postConstruct name="addCipher">
            <arg>lastaflute_core.primaryCipher</arg>
        </postConstruct>
    </component>

    <component name="expPark">
        provider.config().isDevelopmentHere()
            ? "new org.docksidestage.mylasta.trial.TrialSwitchableSea()"
            : "new org.docksidestage.mylasta.trial.TrialSwitchableLand()"
    </component>
    <component name="switchingMaihama" class="org.docksidestage.mylasta.trial.TrialSwitchingMaihama"/>
</components>

Simple Literal

e.g. Di xml simple literal  @Dixml
<property name="simpleString">"dockside"</property>
<property name="simpleInteger">3</property>
<postConstruct name="addStringList">
    <arg>["a","b"]</arg>
</postConstruct>
<postConstruct name="addStringMap">
    <arg>{"sea.over": "land.oneman"}</arg>
</postConstruct>

Reflection Literal

e.g. Di xml reflection literal  @Dixml

<postConstruct name="addType">
    <arg>@org.dbflute.Entity@class</arg>
</postConstruct>
<postConstruct name="addStaticFinal">
    <arg>@jp.sea.SeaPark@STATIC_DEF</arg>
</postConstruct>
<postConstruct name="addStaticMethod">
    <arg>@jp.sea.SeaPark@callStatic()</arg>
</postConstruct>
<postConstruct name="addConfigProp">
    <arg>provider.config().getJdbcUrl()</arg>
</postConstruct>

Component Reference

e.g. Di xml reflection literal  @Dixml
<component name="sea" class="org.docksidestage.mylasta.trial.TrialDiSea"/>
<component name="land" class="org.docksidestage.mylasta.trial.TrialDiLand"/>
<component name="parks">[sea, land]</component>
<component name="maihama" class="org.docksidestage.mylasta.trial.TrialDiMaihama">
    <property name="parkList">parks</property>
</component>

...
<component name="expSea" class="org.docksidestage.mylasta.trial.TrialExpSea">
    ...
    <postConstruct name="addConfigProp">
        <arg>provider.config().getJdbcUrl()</arg>
    </postConstruct>
    <postConstruct name="addCipher">
        <arg>lastaflute_core.primaryCipher</arg>
    </postConstruct>
    ...
</component>

リスト内のコンポーネント参照は、すべての要素がコンポーネントである必要があります。

同じファイル内、もしくは、include先のコンポーネントを参照できます。その世界の中でユニークなコンポーネント名であれば、そのまま記述できます。 ユニークではない、もしくは、紛れがおきないようにしたいとかであれば、lastaflute_core.primaryCipher というように、namespaceを指定します。

Condition Operand

e.g. Di xml reflection literal  @Dixml
<include condition="#exists('#path')" path="trial_option.xml"/>
<include condition="#SMART == 'alwaysNotMatch'" path="rdb.xml"/>

...

<component name="switchedPark">
    provider.config().isDevelopmentHere()
        ? "new org.docksidestage.mylasta.trial.TrialSwitchableSea()"
        : "new org.docksidestage.mylasta.trial.TrialSwitchableLand()"
</component>

"xmlファイルが存在していたら include する" を、#exists() を使うことで定型的に書けます。

conditionの中で、'==' 演算子も利用できます。#SMART は、'cool', 'hot', 'warm' の三つのどれか。(Exampleでは、絶対にヒットしないようになっています)

三項演算子で new するときは、new部分をダブルクォーテーションで囲います。 三項演算子で切り替えたコンポーネントも、他のコンポーネントと同様に、@Resource の private フィールドでDIできます。

TODO jflute もっと整理する。