将.properties文件与Custom Bean Parser一起使用

时间:2012-10-17 14:00:35

标签: java spring scala customization javabeans

我有一个AbstractSingleBeanDefinitionParser的自定义实现,允许我在spring spring配置中定义3D Vectors,其中包含的内容比其他所需要的少。

<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)

当我将.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))
  }
}

如果有必要,我很乐意添加密钥替换,我只需要知道在哪里/怎么做。

想法?

1 个答案:

答案 0 :(得分:4)

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

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

    (当然其他事情也会发生,但这些是相关的步骤。)

    因此,为了将已解析的属性值放入Vector3D对象中,我认为在BeanFactoryPostProcessors运行之前,您将不得不延迟指定Vector3D构造函数的参数。我遇到的一种方法是让你的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;
        }
    }
    

    然后你的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)
      }
    }
    

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