使用带有自定义 Bean 解析器的 .properties 文件

fbl*_*fbl 5 java customization spring scala javabeans

我有一个 AbstractSingleBeanDefinitionParser 的自定义实现,以允许我在我的 spring 配置中定义 3D 矢量,而比其他方式所需的仪式更少。

<rbf:vector3d id="test_vector" delimeter=";" value="45;46;47"/>
Run Code Online (Sandbox Code Playgroud)

效果很好,我已经使用它几个月了,没有任何问题。昨天我尝试在 .properties 文件中定义值,如下所示:

在 test.properties 我有:

vector3d.value=1,2,3
Run Code Online (Sandbox Code Playgroud)

在 xml 文件中我有:

<context:property-placeholder location="test.properties"/>
<rbf:vector3d id="test_vector_with_properties" delimeter="," value="${vector3d.value}"/>
Run Code Online (Sandbox Code Playgroud)

当我尝试运行我的单元测试时,它崩溃了,我得到了这个异常:

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)
Run Code Online (Sandbox Code Playgroud)

当我将 .properties 文件用于普通 bean 时,它工作得很好,这让我相信在我的解析器实现中我忽略了一个微妙之处。它是用 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))
  }
}
Run Code Online (Sandbox Code Playgroud)

如有必要,我很高兴添加密钥替换,我只需要知道在哪里/如何做。

想法?

mat*_*tts 5

所以这不起作用的原因是您的 BeanDefinitionParser 在解决属性占位符之前运行了很多。我理解的简单概述:

  1. BeanDefinitionParsers 将 XML 解析为内存中的 BeanDefinition 对象
  2. 然后将 BeanDefinitions 加载到 BeanFactory
  3. BeanFactoryPostProcessors(包括属性占位符配置器)在 bean 定义上执行
  4. bean 是根据 bean 定义创建的

(当然,在此过程中还会发生其他事情,但这些是此处的相关步骤。)

因此,为了将解析的属性值放入您的 Vector3D 对象,我认为您将不得不延迟指定 Vector3D 构造函数的参数,直到 BeanFactoryPostProcessors 运行之后。我想到的一种方法是让您的 BeanDefinitionParser 为 Spring FactoryBean 而不是 Vector3D 本身构造一个 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的 Vector3DBeanDefinitionParser 只会将delimitervalue原封不动地传递给 Vector3DFactoryBean bean 定义:

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

然后当占位符属性配置器运行时,它应该解析 Vector3DFactoryBean bean 定义中的属性值。当 bean 最终根据 bean 定义创建时,Vector3DFactoryBean 将解析向量值并创建 Vector3D 对象。