2009-01-26 6 views
8

Spring hat eine sehr praktische Convenience-Klasse namens PropertyPlaceholderConfigurer, die eine Standarddatei .properties verwendet und Werte aus dieser in Ihre Bean.xml-Konfiguration einfügt.Gibt es eine PropertyPlaceholderConfigurer-ähnliche Klasse zur Verwendung mit Spring, die XML akzeptiert?

Kennt jemand eine Klasse, die genau dasselbe tut, und integriert sich mit Spring in der gleichen Weise, aber akzeptiert XML-Dateien für die Konfig. Insbesondere denke ich an Apache Digester-Config-Dateien. Es wäre einfach genug, dies zu tun, ich frage mich nur, ob jemand schon hat.

Vorschläge?

Antwort

7

Ich habe das gerade getestet, und es sollte einfach funktionieren.

PropertiesPlaceholderConfigurer enthält eine setPropertiesPersister Methode, so dass Sie Ihre eigene Unterklasse von PropertiesPersister verwenden können. Der Standard PropertiesPersister unterstützt bereits Eigenschaften im XML-Format.

Nur um zeigen Sie den voll funktionierenden Code:

JUnit 4.4 Testfall:

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()); 
    } 
} 

Die Feder Konfigurationsdatei 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> 

Die XML-Eigenschaften properties.xml Datei - siehe here für Beschreibung der Nutzung.

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

Und schließlich die Bohne:

package org.nkl; 

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

Hope this helps ...

+0

das auf jeden Fall praktisch zu wissen, und wird ein handliches Backup sein.Ich denke, es ist einfach genug, PropertiesPersister außer Kraft zu setzen, um das Apache Digester-Stil-Parsing statt des Standard-XML-Formats zu implementieren. – GaryF

+0

Was würde es dauern, um etwas zu erstellen, das benutzerdefinierte (Nicht-Eigenschaften) XML-Dateien mit Ressourcenladeprogrammen lädt? –

4

Fanden heraus, dass Federmodule integration between Spring and Commons Configuration, bereitzustellen, die einen hierarchischen XML-Konfigurations Stil. Dies geht direkt in PropertyPlaceholderConfigurer, was genau das ist, was ich wollte.

+0

Nice one GaryF - +1 :-) – toolkit

+0

Link verweist nicht mehr auf ein bestimmtes Beispiel? –

2

Ich bin nicht sicher über die Apache Digestor-Config-Dateien, aber ich fand eine Lösung, die nicht so schwer zu implementieren und passend für meine XML-Config-Datei war.

Sie können den normalen PropertyPlaceholderConfigurer von spring verwenden, aber um Ihre benutzerdefinierte Konfiguration zu lesen, müssen Sie einen eigenen PropertiesPersister erstellen, in dem Sie das XML (mit XPath) analysieren und die erforderlichen Eigenschaften selbst festlegen können.

Hier ist ein kleines Beispiel:

zuerst Ihre eigenen PropertiesPersister erstellen, indem Sie die Standard-Verlängerung ein:

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) 
     { 
      ... 
     } 
    } 

Dann injizieren die CustomPropertiesPersister zum PropertyPlaceholderConfigurer im Anwendungskontext:

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

Danach können Sie Ihre Eigenschaften wie folgt verwenden:

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

mich selbst versucht, dies mit einer schönen Lösung zu kommen, dass

  1. Revolves um ein XSD für die Config-Datei erstellen - da das gesamte Nutzen XML der Verwendung in meinem Kopf ist, dass Sie stark eingeben können die Konfigurationsdatei in Bezug auf Datentypen und welche Felder obligatorisch/optional sind
  2. Validiert die XML gegen die XSD, wenn also ein Wert fehlt, wird ein Fehler ausgegeben, anstatt dass Ihre Bean mit einem 'Null' injiziert wird '
  3. Stützt sich nicht auf Spring-Annotationen (wie @Value - in meinen Augen gibt das Bohnen Wissen abo ut ihre container + Beziehung mit anderen Bohnen, so bricht IOC)
  4. Will validieren die Feder XML gegen die XSD, wenn Sie also versuchen, ein XML-Feld verweisen nicht in der XSD, wird es auch einen Fehler zu werfen
  5. Die Bean weiß nicht, dass ihre Eigenschaftswerte aus XML importiert werden (d. H Ich möchte auf einzelne Eigenschaften injizieren, und nicht das XML-Objekt als Ganzes)

Was ich kam mit, wie unten ist, Entschuldigungen dies ziemlich langatmig, aber Ich mag es als eine Lösung, da ich glaube, es deckt alles. Hoffentlich könnte sich das für jemanden als nützlich erweisen. Trivial Stücke zuerst:

Die Bohne I Eigenschaftswerte in injiziert werden soll:

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; 
    } 
} 

Test-Klasse, um die oben Bean zu erstellen und die Eigenschaftswerte auskippen es bekam:

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> 

Beispiel MyConfig.xml Datei auf dem XSD basiert:

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

Snippet pom.xml Datei xsd2java zu laufen (war hier nicht viel anderes als Java-Einstellung 1,6 und Feder-Kontextabhängigkeit):

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

nun die Feder XML selbst. Dadurch entsteht ein Schema/Validator, dann JAXB verwendet einen Unmarshaller erstellen POJO aus der XML-Datei zu erstellen, dann spring # Anmerkung verwendet, indem Quering die POJO Eigenschaftswert zu injizieren:

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