在自定义Bean解析器中使用.properties文件 - java

我有一个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)

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

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

有想法吗?

参考方案

因此,这样做不起作用的原因是,您的BeanDefinitionParser会在解析属性占位符之前运行很多。据我了解的简单概述:

  • BeanDefinitionParsers将XML解析为内存中的BeanDefinition对象
  • 然后将

  • BeanDefinitions加载到BeanFactory中
  • BeanFactoryPostProcessors(包括属性占位符配置器)在bean定义
  • 上执行

  • bean是根据bean定义
  • 创建的

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

    因此,为了将解析的属性值输入到Vector3D对象中,我认为您将不得不延迟为Vector3D构造函数指定参数,直到BeanFactoryPostProcessors运行之后。我想到的一种方法是让BeanDefinitionParser构造一个Spring FactoryBean的bean定义,而不是Vector3D本身。然后,您当前在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)
      }
    }
    

    然后,当占位符属性配置程序运行时,它将解析Vector3DFactoryBean bean定义中的属性值。当最终通过bean定义创建bean时,Vector3DFactoryBean将解析矢量值并创建Vector3D对象。

    Java-搜索字符串数组中的字符串 - java

    在Java中,我们是否有任何方法可以发现特定字符串是字符串数组的一部分。我可以避免出现一个循环。例如String [] array = {"AA","BB","CC" }; string x = "BB" 我想要一个if (some condition to tell wheth…

    Java Scanner读取文件的奇怪行为 - java

    因此,在使用Scanner类从文件读取内容时,我遇到了一个有趣的问题。基本上,我试图从目录中读取解析应用程序生成的多个输出文件,以计算一些准确性指标。基本上,我的代码只是遍历目录中的每个文件,并使用扫描仪将其打开以处理内容。无论出于何种原因,扫描程序都不会读取其中的一些文件(所有UTF-8编码)。即使文件不是空的,scanner.hasNextLine()在…

    Java Globbing模式以匹配目录和文件 - java

    我正在使用递归函数遍历根目录下的文件。我只想提取*.txt文件,但不想排除目录。现在,我的代码如下所示:val stream = Files.newDirectoryStream(head, "*.txt") 但是这样做将不会匹配任何目录,并且返回的iterator()是False。我使用的是Mac,所以我不想包含的噪音文件是.DS_ST…

    直接读取Zip文件中的文件-Java - java

    我的情况是我有一个包含一些文件(txt,png,...)的zip文件,我想直接按它们的名称读取它,我已经测试了以下代码,但没有结果(NullPointerExcepion):InputStream in = Main.class.getResourceAsStream("/resouces/zipfile/test.txt"); Buff…

    Java RegEx中的单词边界\ b - java

    我在使用\b作为Java Regex中的单词定界符时遇到困难。对于text = "/* sql statement */ INSERT INTO someTable"; Pattern.compile("(?i)\binsert\b");找不到匹配项Pattern insPtrn = Pattern.compile(&…