Существует ли класс, подобный PropertyPlaceholderConfigurer, для использования с Spring, который принимает XML?

Spring имеет очень удобный удобный класс под названием PropertyPlaceholderConfigurer, который берет стандартный файл .properties и вставляет значения из него в вашу конфигурацию bean.xml.

Кто-нибудь знает класс, который делает то же самое и точно так же интегрируется со Spring, но принимает XML-файлы для конфигурации. В частности, я имею в виду конфигурационные файлы в стиле дайджеста Apache. Это было бы достаточно легко сделать, мне просто интересно, если кто-то уже сделал.

Предложения?


person GaryF    schedule 26.01.2009    source источник


Ответы (4)


Я только что проверил это, и это должно просто работать.

PropertiesPlaceholderConfigurer содержит метод setPropertiesPersister, поэтому вы можете использовать свой собственный подкласс PropertiesPersister. По умолчанию PropertiesPersister уже поддерживает свойства в формате XML.

Просто чтобы показать вам полностью рабочий код:

Тестовый пример JUnit 4.4:

package org.nkl;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration(locations = { "classpath:/org/nkl/test-config.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class PropertyTest {

    @Autowired
    private Bean bean;

    @Test
    public void testPropertyPlaceholderConfigurer() {
        assertNotNull(bean);
        assertEquals("fred", bean.getName());
    }
}

Конфигурационный файл весны test-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
">
  <context:property-placeholder 
      location="classpath:/org/nkl/properties.xml" />
  <bean id="bean" class="org.nkl.Bean">
    <property name="name" value="${org.nkl.name}" />
  </bean>
</beans>

Файл свойств XML properties.xml — описание использования см. здесь. .

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
  <entry key="org.nkl.name">fred</entry>
</properties>

И, наконец, фасоль:

package org.nkl;

public class Bean {
    private String name;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

Надеюсь это поможет...

person toolkit    schedule 26.01.2009
comment
Это определенно полезно знать, и это будет удобной резервной копией. Я предполагаю, что достаточно легко переопределить PropertiesPersister для реализации синтаксического анализа в стиле Apache Digester вместо стандартного формата xml свойств. - person GaryF; 27.01.2009
comment
что потребуется, чтобы создать что-то, что загружает пользовательские (не свойства) файлы xml с помощью загрузчиков ресурсов? - person Kalpesh Soni; 27.05.2015

Выяснилось, что модули Spring обеспечивают интеграцию между Spring и Commons Configuration, что имеет иерархический стиль конфигурации XML. Это напрямую связано с PropertyPlaceholderConfigurer, чего я и хотел.

person GaryF    schedule 29.01.2009

Пытался придумать хорошее решение для себя, что

  1. Вращается вокруг создания XSD для файла конфигурации, поскольку, на мой взгляд, все преимущество использования XML заключается в том, что вы можете строго типизировать файл конфигурации с точки зрения типов данных и того, какие поля являются обязательными/необязательными.
  2. Будет проверять XML по XSD, поэтому, если значение отсутствует, оно выдаст ошибку, а не ваш bean-компонент будет введен с «нулевым»
  3. Не полагается на весенние аннотации (например, @Value - на мой взгляд, это дает bean-компонентам знания об их контейнере + отношения с другими bean-компонентами, поэтому нарушает IOC)
  4. Будет проверять пружинный XML на соответствие XSD, поэтому, если вы попытаетесь сослаться на поле XML, отсутствующее в XSD, оно также выдаст ошибку.
  5. Бин не знает, что значения его свойств вводятся из XML (т.е. я хочу вводить отдельные свойства, а не объект XML в целом)

То, что я придумал, выглядит так, как показано ниже, извиняюсь, это довольно многословно, но мне это нравится как решение, поскольку я считаю, что оно охватывает все. Надеюсь, это может оказаться полезным для кого-то. Сначала тривиальные фрагменты:

Компонент, в который я хочу ввести значения свойств:

package com.ndg.xmlpropertyinjectionexample;

public final class MyBean
{
    private String firstMessage;
    private String secondMessage;

    public final String getFirstMessage ()
    {
        return firstMessage;
    }

    public final void setFirstMessage (String firstMessage)
    {
        this.firstMessage = firstMessage;
    }

    public final String getSecondMessage ()
    {
        return secondMessage;
    }

    public final void setSecondMessage (String secondMessage)
    {
        this.secondMessage = secondMessage;
    }
}

Тестовый класс для создания указанного выше bean-компонента и вывода полученных им значений свойств:

package com.ndg.xmlpropertyinjectionexample;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Main
{
    public final static void main (String [] args)
    {
        try
        {
            final ApplicationContext ctx = new ClassPathXmlApplicationContext ("spring-beans.xml");
            final MyBean bean = (MyBean) ctx.getBean ("myBean");
            System.out.println (bean.getFirstMessage ());
            System.out.println (bean.getSecondMessage ());
        }
        catch (final Exception e)
        {
            e.printStackTrace ();
        }
    }

}

MyConfig.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:myconfig="http://ndg.com/xmlpropertyinjectionexample/config" targetNamespace="http://ndg.com/xmlpropertyinjectionexample/config">

    <xsd:element name="myConfig">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element minOccurs="1" maxOccurs="1" name="someConfigValue" type="xsd:normalizedString" />
                <xsd:element minOccurs="1" maxOccurs="1" name="someOtherConfigValue" type="xsd:normalizedString" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

Пример файла MyConfig.xml на основе XSD:

<?xml version="1.0" encoding="UTF-8"?>
<config:myConfig xmlns:config="http://ndg.com/xmlpropertyinjectionexample/config">
    <someConfigValue>First value from XML file</someConfigValue>
    <someOtherConfigValue>Second value from XML file</someOtherConfigValue>
</config:myConfig>

Фрагмент файла pom.xml для запуска xsd2java (здесь не было ничего, кроме настройки на Java 1.6 и зависимости от spring-контекста):

        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <executions>
                <execution>
                    <id>main-xjc-generate</id>
                    <phase>generate-sources</phase>
                    <goals><goal>generate</goal></goals>
                </execution>
            </executions>
        </plugin>

Теперь сам Spring XML. Это создает схему/валидатор, затем использует JAXB для создания unmarshaller для создания POJO из файла XML, а затем использует аннотацию spring # для ввода значений свойств путем запроса POJO:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd" >

    <!-- Set up schema to validate the XML -->

    <bean id="schemaFactory" class="javax.xml.validation.SchemaFactory" factory-method="newInstance">
        <constructor-arg value="http://www.w3.org/2001/XMLSchema"/>
    </bean> 

    <bean id="configSchema" class="javax.xml.validation.Schema" factory-bean="schemaFactory" factory-method="newSchema">
        <constructor-arg value="MyConfig.xsd"/>
    </bean>

    <!-- Load config XML -->

    <bean id="configJaxbContext" class="javax.xml.bind.JAXBContext" factory-method="newInstance">
        <constructor-arg>
            <list>
                <value>com.ndg.xmlpropertyinjectionexample.config.MyConfig</value>
            </list>
        </constructor-arg>
    </bean>

    <bean id="configUnmarshaller" class="javax.xml.bind.Unmarshaller" factory-bean="configJaxbContext" factory-method="createUnmarshaller">
        <property name="schema" ref="configSchema" />
    </bean>

    <bean id="myConfig" class="com.ndg.xmlpropertyinjectionexample.config.MyConfig" factory-bean="configUnmarshaller" factory-method="unmarshal">
        <constructor-arg value="MyConfig.xml" />
    </bean>

    <!-- Example bean that we want config properties injected into -->

    <bean id="myBean" class="com.ndg.xmlpropertyinjectionexample.MyBean">
        <property name="firstMessage" value="#{myConfig.someConfigValue}" />
        <property name="secondMessage" value="#{myConfig.someOtherConfigValue}" />
    </bean>

</beans>
person nigelg    schedule 05.05.2013

Я не уверен в конфигурационных файлах в стиле дайджеста Apache, но я нашел решение, которое было не так сложно реализовать и которое подходит для моего конфигурационного файла xml.

Вы можете использовать обычный PropertyPlaceholderConfigurer из весны, но чтобы прочитать свою пользовательскую конфигурацию, вам нужно создать свой собственный PropertiesPersister, где вы можете проанализировать xml (с помощью XPath) и самостоятельно установить необходимые свойства.

Вот небольшой пример:

Сначала создайте свой собственный PropertiesPersister, расширив стандартный:

public class CustomXMLPropertiesPersister extends DefaultPropertiesPersister {
            private XPath dbPath;
            private XPath dbName;
            private XPath dbUsername;
            private XPath dbPassword;

            public CustomXMLPropertiesPersister() throws JDOMException  {
                super();

            dbPath = XPath.newInstance("//Configuration/Database/Path");
            dbName = XPath.newInstance("//Configuration/Database/Filename");
            dbUsername = XPath.newInstance("//Configuration/Database/User");
            dbPassword = XPath.newInstance("//Configuration/Database/Password");
        }

        public void loadFromXml(Properties props, InputStream is)
        {
            Element rootElem = inputStreamToElement(is);

            String path = "";
            String name = "";
            String user = "";
            String password = "";

            try
            {
                path = ((Element) dbPath.selectSingleNode(rootElem)).getValue();
                name = ((Element) dbName.selectSingleNode(rootElem)).getValue();
                user = ((Element) dbUsername.selectSingleNode(rootElem)).getValue();
                password = ((Element) dbPassword.selectSingleNode(rootElem)).getValue();
            }
            catch (JDOMException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            props.setProperty("db.path", path);
            props.setProperty("db.name", name);
            props.setProperty("db.user", user);
            props.setProperty("db.password", password);
        }

        public Element inputStreamToElement(InputStream is)
        {       
            ...
        }

        public void storeToXml(Properties props, OutputStream os, String header)
        {
            ...
        }
    }

Затем введите CustomPropertiesPersister в PropertyPlaceholderConfigurer в контексте приложения:

<beans ...>
    <bean id="customXMLPropertiesPersister" class="some.package.CustomXMLPropertiesPersister" />

    <bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_FALLBACK" />
        <property name="location" value="file:/location/of/the/config/file" />
        <property name="propertiesPersister" ref="customXMLPropertiesPersister" />
    </bean> 
</beans>

После этого вы можете использовать свои свойства следующим образом:

<bean id="someid" class="some.example.class">
  <property name="someValue" value="$(db.name)" />
</bean>
person Michael    schedule 25.05.2011