2009-01-26 10 views
8

Spring tiene una clase de conveniencia muy útil llamada PropertyPlaceholderConfigurer, que toma un archivo .properties estándar e inyecta valores desde él en su configuración bean.xml.¿Hay una clase similar a PropertyPlaceholderConfigurer para usar con Spring que acepta XML?

Alguien sabe de una clase que hace exactamente lo mismo, y se integra con Spring de la misma manera, pero acepta archivos XML para la configuración. Específicamente, estoy pensando en los archivos de configuración estilo digestor de Apache. Sería bastante fácil hacer esto, solo me pregunto si alguien ya lo hizo.

Sugerencias?

Respuesta

7

Acabo de probar esto, y debería funcionar.

PropertiesPlaceholderConfigurer contiene un método setPropertiesPersister, por lo que puede usar su propia subclase de PropertiesPersister. El valor predeterminado de PropertiesPersister ya admite propiedades en formato XML.

sólo para mostrar el código completamente de trabajo:

JUnit caso 4.4 prueba:

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

El archivo de configuración de la primavera de archivo 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> 

Las propiedades XML properties.xml - ver here de descripción del uso.

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

Y, finalmente, el frijol:

package org.nkl; 

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

Espero que esto ayude ...

+0

Eso es sin duda muy útil para saber, y será una práctica copia de seguridad.Supongo que es bastante fácil anular PropertiesPersister para implementar el análisis del estilo de Apache Digester, en lugar del formato xml de propiedades estándar. – GaryF

+0

¿Qué se necesita para crear algo que cargue archivos xml personalizados (sin propiedades) usando cargadores de recursos? –

4

dimos cuenta de que los módulos de Primavera proporcionar integration between Spring and Commons Configuration, que tiene un estilo de configuración XML jerárquico. Esto se relaciona directamente con PropertyPlaceholderConfigurer, que es exactamente lo que yo quería.

+0

Nice one GaryF - +1 :-) – toolkit

+0

¿El enlace ya no apunta al ejemplo particular? –

2

No estoy seguro acerca de los archivos de configuración del estilo del digestor Apache, pero encontré una solución que no fue tan difícil de implementar y adecuada para mi archivo de configuración xml.

Puede usar el PropertyPlaceholderConfigurer normal desde la primavera, pero para leer su configuración personalizada debe crear su propia PropertiesPersister, donde puede analizar el xml (con XPath) y establecer las propiedades necesarias usted mismo.

Aquí hay un pequeño ejemplo:

En primer lugar crear su propia PropertiesPersister extendiendo el que viene por defecto:

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

Luego se inyectan la CustomPropertiesPersister a la PropertyPlaceholderConfigurer en el contexto de aplicación:

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

Después de eso, puede usar sus propiedades de esta manera:

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

estado tratando de llegar a una buena solución a esto mismo que

  1. gira en torno a la creación de un XSD para el archivo de configuración - ya que en mi mente todo el beneficio del uso de XML es que puede escribir fuertemente el archivo de configuración, en términos de tipos de datos, y qué campos son obligatorios/opcionales
  2. Validará el XML contra el XSD, por lo que si falta un valor arrojará un error en lugar de que su bean sea inyectado con un 'nulo '
  3. No confía en las anotaciones de primavera (como @Value - en mi opinión eso le da conocimiento sobre frijoles ut su contenedor + relación con otros beans, por lo que rompe COI)
  4. Validará el XML de primavera frente al XSD, por lo que si intenta hacer referencia a un campo XML no presente en el XSD, arrojará también un error
  5. El bean no tiene conocimiento de que sus valores de propiedad se estén inyectando desde XML (es decir, Quiero inyectar propiedades individuales, y no el objeto XML como un todo)

Lo que se me ocurrió es la siguiente, disculpas, esto es bastante largo, pero me gusta como una solución ya que creo que cubre todo. Espero que esto pueda serle útil a alguien. piezas triviales primeros:

El grano Quiero valores de las propiedades inyectados en:

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

clase de prueba para crear el grano de arriba y volcar los valores de propiedad que ya ha recibido:

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 : archivo MyConfig.xml

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

Muestra basado en el 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> 

de fragmentos de pom.xml para funcionar xsd2java (no había mucho más aquí además de establecer a Java 1.6, y la dependencia de primavera-contexto):

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

Ahora el XML propio muelle. Esto crea un esquema/validador, a continuación, utiliza JAXB para crear un unmarshaller para crear un POJO del archivo XML, a continuación, utiliza la primavera # anotación para inyectar valores de propiedad por quering el 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> 
Cuestiones relacionadas