5

私は、AbstractSingleBeanDefinitionParser のカスタム実装を使用して、Spring 構成で 3D ベクトルを定義できるようにしています。

<rbf:vector3d id="test_vector" delimeter=";" value="45;46;47"/>

それはうまく機能し、何ヶ月も問題なく使用しています. 昨日、次のように .properties ファイルで値を定義しようとしました。

test.properties には次のものがあります。

vector3d.value=1,2,3

そして、私が持っているxmlファイルには:

<context:property-placeholder location="test.properties"/>
<rbf:vector3d id="test_vector_with_properties" delimeter="," value="${vector3d.value}"/>

単体テストを実行しようとするとクラッシュし、次の例外が発生します。

Caused by: java.lang.NumberFormatException: For input string: "${vector3d.value}"
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1222)
    at java.lang.Double.parseDouble(Double.java:510)
    at scala.collection.immutable.StringLike$class.toDouble(StringLike.scala:234)
    at scala.collection.immutable.StringOps.toDouble(StringOps.scala:31)
    at rb.foundation.spring.xml.Vector3DBeanDefinitionParser$$anonfun$1.apply(Vector3DBeanDefinitionParser.scala:25)

通常の Bean に .properties ファイルを使用すると、うまく機能するので、パーサーの実装で見落としていた微妙な点があると思います。これはscalaで書かれていますが、それに従うことができるはずです:

class Vector3DBeanDefinitionParser extends AbstractSingleBeanDefinitionParser
{
  override def getBeanClass(element : Element) = classOf[Vector3D]

  override def doParse(element: Element, builder: BeanDefinitionBuilder)
  {
    val delim = element.getAttribute("delimeter")
    val value = element.getAttribute("value")

    val values = value.split(delim).map(_.toDouble)

    builder.addConstructorArgValue(values(0))
    builder.addConstructorArgValue(values(1))
    builder.addConstructorArgValue(values(2))
  }
}

必要に応じて、キーの置換を喜んで追加します。それをどこでどのように行うかを知る必要があるだけです。

アイデア?

4

1 に答える 1

5

したがって、これが機能しない理由は、プロパティのプレースホルダーが解決されるずっと前に BeanDefinitionParser が実行されるためです。私が理解している簡単な概要:

  1. BeanDefinitionParsers は、XML をメモリ内の BeanDefinition オブジェクトに解析します
  2. 次に、BeanDefinitions が BeanFactory にロードされます
  3. BeanFactoryPostProcessors (プロパティ プレースホルダー コンフィギュアラーを含む) は、Bean 定義で実行されます。
  4. Bean は Bean 定義から作成されます

(もちろん、途中で他のことも起こりますが、それらはここでの関連するステップです。)

したがって、解決されたプロパティ値を Vector3D オブジェクトに取得するには、BeanFactoryPostProcessors が実行されるまで、Vector3D コンストラクターへの引数の指定を遅らせる必要があると思います。私が思いつく 1 つの方法は、Vector3D 自体ではなく、BeanDefinitionParser に Spring FactoryBean の Bean 定義を作成させることです。次に、現在 Vector3DBeanDefinitionParser にあるベクトル値の分割は、代わりに FactoryBean 実装にある必要があります。

申し訳ありませんが、私は Scala にあまり詳しくないので、これは Java で行います。

FactoryBean クラスは次のようになります。

import org.springframework.beans.factory.FactoryBean;

public class Vector3DFactoryBean implements FactoryBean<Vector3D> {
    private String delimiter;
    private String value;
    private transient Vector3D instance;

    public String getDelimiter() { return delimiter; }
    public void setDelimiter(String delimiter) { this.delimiter = delimiter; }
    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }

    @Override
    public Vector3D getObject() {
        if (instance == null) {
            String[] values = value.split(delimiter);
            instance = new Vector3D(
                                    Double.parseDouble(values[0]),
                                    Double.parseDouble(values[1]),
                                    Double.parseDouble(values[2])
                                   );
        }
        return instance;
    }
    @Override
    public Class<?> getObjectType() {
        return Vector3D.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}

次に、Vector3DBeanDefinitionParser はそのまま渡して Vector3DFactoryBean Bean 定義にdelimiter渡します。value

class Vector3DBeanDefinitionParser extends AbstractSingleBeanDefinitionParser
{
  override def getBeanClass(element : Element) = classOf[Vector3DFactoryBean]

  override def doParse(element: Element, builder: BeanDefinitionBuilder)
  {
    val delim = element.getAttribute("delimeter")
    val value = element.getAttribute("value")

    builder.addPropertyValue("delimiter", delim)
    builder.addPropertyValue("value", value)
  }
}

その後、プレースホルダー プロパティ コンフィギュアラーが実行されると、Vector3DFactoryBean Bean 定義のプロパティ値が解決されます。Bean が最終的に Bean 定義から作成されると、Vector3DFactoryBean はベクター値を解析し、Vector3D オブジェクトを作成します。

于 2012-10-22T21:43:03.943 に答える