2009-01-26 14 views
8

Wiosna ma bardzo poręczną klasę wygody o nazwie PropertyPlaceholderConfigurer, która pobiera standardowy plik .properties i wprowadza wartości z niego do pliku konfiguracyjnego bean.xml.Czy istnieje klasa podobna do właściwości PropertyPlaceholderConfigurer do użytku ze Spring, która akceptuje XML?

Czy ktoś wie o klasie, która robi dokładnie to samo i integruje się ze Springem w ten sam sposób, ale akceptuje pliki XML dla konfiguracji. Mówiąc konkretnie, myślę o plikach konfiguracyjnych w stylu Digietera. Łatwo byłoby to zrobić, zastanawiam się tylko, czy ktoś już to zrobił.

Sugestie?

Odpowiedz

7

Właśnie to przetestowałem i powinno po prostu zadziałać.

PropertiesPlaceholderConfigurer zawiera metodę setPropertiesPersister, dzięki czemu można użyć własnej podklasy PropertiesPersister. Domyślna właściwość PropertiesPersister obsługuje już właściwości w formacie XML.

Wystarczy pokazać kod pełni funkcjonalny:

JUnit przypadek 4,4 Test:

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

Plik wiosna config 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> 

Właściwości pliku XML properties.xml - patrz here dla opis użycia.

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

I wreszcie fasola:

package org.nkl; 

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

Nadzieja to pomaga ...

+0

To zdecydowanie przydatny wiedzieć, a będzie przydatnym wsparciem.Domyślam się, że łatwo jest przesłonić PropertiesPersister, aby zaimplementować parsowanie stylu Apache Digester, zamiast standardowego formatu XML właściwości. – GaryF

+0

Co trzeba zrobić, aby utworzyć coś, co ładuje niestandardowe (nie własności) pliki xml przy użyciu modułów ładujących zasoby? –

4

okazało się, że wiosna Moduły zapewniają integration between Spring and Commons Configuration, która ma hierarchicznych styl konfiguracyjnego XML. To wiąże się bezpośrednio z PropertyPlaceholderConfigurer, co jest dokładnie tym, czego chciałem.

+0

Nice one GaryF - +1 :-) – toolkit

+0

link nie odnosi się już do konkretnego przykładu? –

2

Nie jestem pewien co do plików konfiguracyjnych w stylu Degersa, ale znalazłem rozwiązanie, które nie było trudne do wdrożenia i odpowiednie dla mojego pliku konfiguracyjnego xml.

Możesz użyć normalnego PropertyPlaceholderConfigurer od wiosny, ale aby odczytać konfigurację niestandardową, musisz utworzyć własny PropertiesPersister, w którym możesz sparsować xml (z XPath) i ustawić wymagane właściwości samemu.

Oto mały przykład:

Najpierw utwórz własną PropertiesPersister rozszerzając domyślną:

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

następnie zastosować CustomPropertiesPersister do PropertyPlaceholderConfigurer w kontekście aplikacji:

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

Następnie możesz użyć swoich właściwości w następujący sposób:

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

próbuje wymyślić ładnym rozwiązaniem tego sobie, że

  1. kręci się wokół tworzenia XSD dla pliku konfiguracyjnym - ponieważ w mojej głowie cały zaletą korzystania z XML jest to, że można mocno wpisać plik konfiguracyjny pod względem typów danych i pól, które są obowiązkowe/opcjonalne
  2. Sprawdza poprawność kodu XML w stosunku do XSD, więc jeśli brakuje wartości, wygeneruje błąd, zamiast wstrzykiwania komponentu bean z wartością "null" "
  3. Nie polega na adnotacjach wiosennych (takich jak @Value - w moim umyśle daje to wiedzę o fasoli na temat ut ich pojemnik + relacja z innymi ziarnami, więc łamie IOC)
  4. Sprawdza wartość sprężyny XML względem XSD, więc jeśli spróbujesz odwołać się do pola XML nieobecnego w XSD, to wyrzuci on również błąd
  5. Ziarno nie ma wiedzy, że jego wartości właściwości są wstrzykiwane z XML (np. Chcę wstrzyknąć poszczególne właściwości, a nie obiekt XML jako całość)

To, co wymyśliłem, to tak, jak poniżej, przeprosiny są dość długie, ale podoba mi się to jako rozwiązanie, ponieważ uważam, że obejmuje wszystko. Mam nadzieję, że może się to okazać przydatne dla kogoś. Pierwsze kawałki trywialne:

Fasola Chcę wartości nieruchomości wstrzykiwane do:

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

klasy testowym stworzyć powyższy fasoli i zrzucić wartości właściwości to otrzymała:

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> 

Próbka MyConfig.xml plik na podstawie 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> 

fragment pliku pom.xml do uruchomienia xsd2java (nie było nic innego tu oprócz ustawienie Java 1.6, a zależność wiosna-context): Sam

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

Teraz XML wiosny. Stwarza to schematu/walidatora, a następnie wykorzystuje JAXB stworzyć unmarshaller stworzyć POJO z pliku XML, a następnie wykorzystuje wiosna # adnotacji wstrzyknąć wartości nieruchomości przez quering z 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>