Вопрос: Весна, чтобы понять свойства в YAML


Была ли Spring отказаться от использования YAML для использования в качестве альтернативы .properties / .xml из-за:

[Весенний разработчик]: ... Ямл считался, но мы думали, что подсчет пробелов значительным был кошмар поддержки в процессе создания ...  [ссылка из Весенний форум ]

Я уверен, что YAML имеет большой смысл для свойств, и я использую его в настоящее время в проекте, но испытываю трудности с введением свойств в

<property name="productName" value="${client.product.name}" />

мода.

Что-нибудь, что мне не хватает, или я должен создать пользовательскую программу YamlPropertyPlaceholderConfigurer?


9


источник


Ответы:


Я не знаю, было ли это слишком поздно, но нет - вам не нужно реализовывать весь YamlPropertyPlaceholderConfigurer, вместо этого вы можете просто создать собственный PropertyPersister и добавить его в качестве необязательного параметра.

Вот как будет выглядеть ваша конфигурация

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <value>file:///C:/somewhere/site.yaml</value>
    </property>
    <property name="propertiesPersister" ref="persister"></property>
</bean>
<bean id="persister" class="com.foo.utils.YamlPropertiesPersister"></bean>

И вот реализация bare-bone (только для чтения) с использованием SnakeYaml, не стесняйтесь добавлять то, что вам нужно, включая обработку ошибок

public class YamlPropertiesPersister implements PropertiesPersister {
@Override
public void load(Properties props, InputStream is) throws IOException {
    load(props, new InputStreamReader(is));
}

/**
 * We want to traverse map representing Yaml object and each time we find String=String pair we want to
 * save it as Property. As we are going deeper into map we generate compound key as path-like String
 * 
 * @param props
 * @param reader
 * @throws IOException
 * @see org.springframework.util.PropertiesPersister#load(java.util.Properties, java.io.Reader)
 */
@Override
public void load(Properties props, Reader reader) throws IOException {
    Yaml yaml = CollectorUtils.instanceOfYaml();
    Map<String, Object> map = (Map<String, Object>) yaml.load(reader);
    // now we can populate supplied props
    assignProperties(props, map, null);
}

/**
 * @param props
 * @param map
 */
public void assignProperties(Properties props, Map<String, Object> map, String path) {
    for (Entry<String, Object> entry : map.entrySet()) {
        String key = entry.getKey();
        if (StringUtils.isNotEmpty(path))
            key = path + "." + key;
        Object val = entry.getValue();
        if (val instanceof String) {
            // see if we need to create a compound key
            props.put(key, val);
        } else if (val instanceof Map) {
            assignProperties(props, (Map<String, Object>) val, key);
        }
    }
}

@Override
public void store(Properties props, OutputStream os, String header) throws IOException {
    throw new NotImplementedException("Current implementation is a read-only");
}

@Override
public void store(Properties props, Writer writer, String header) throws IOException {
    throw new NotImplementedException("Current implementation is a read-only");
}

@Override
public void loadFromXml(Properties props, InputStream is) throws IOException {
    throw new NotImplementedException("Use DefaultPropertiesPersister if you want to read/write XML");
}

@Override
public void storeToXml(Properties props, OutputStream os, String header) throws IOException {
    throw new NotImplementedException("Use DefaultPropertiesPersister if you want to load/store to XML");
}

@Override
public void storeToXml(Properties props, OutputStream os, String header, String encoding) throws IOException {
    throw new NotImplementedException("Use DefaultPropertiesPersister if you want to read/write XML");
}
}

В качестве дополнительной выгоды - вот как я создаю экземпляр Yaml

    public static Yaml instanceOfYaml() {
    DumperOptions options = new DumperOptions();
    options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
    final Yaml yaml = new Yaml(new Loader(), new Dumper(options), new Resolver() {
        /**
         * @see org.yaml.snakeyaml.resolver.Resolver#addImplicitResolvers()
         */
        @Override
        protected void addImplicitResolvers() {
            addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
            // disable resolving of floats and integers
            // addImplicitResolver(Tags.FLOAT, FLOAT, "-+0123456789.");
            // addImplicitResolver(Tag.INT, INT, "-+0123456789");
            addImplicitResolver(Tag.MERGE, MERGE, "<");
            addImplicitResolver(Tag.NULL, NULL, "~nN\0");
            addImplicitResolver(Tag.NULL, EMPTY, null);
            addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
            addImplicitResolver(Tag.VALUE, VALUE, "=");
        }
    });
    return yaml;
}

Вы также можете прочитать это в мой блог


9



Для тех, кто использует Spring 3.1, вы можете зарегистрировать источник Yaml PropetySource. Код SnakeYaml - это код Bostone (спасибо), адаптированный к новой системе PropertySource Spring 3.1.

import com.google.common.base.Preconditions;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Dumper;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.resolver.Resolver;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

/**
 * @author Sebastien Lorber <i>(lorber.sebastien@gmail.com)</i>
 */
public class YamlPropertiesSource extends PropertiesPropertySource {


  public YamlPropertiesSource(String name, Resource yamlResource) {
    super(name, getPropertySource(yamlResource) );
  }

  private static Properties getPropertySource(Resource yamlResource) {
    Preconditions.checkArgument(yamlResource != null,"no yaml resource provided");
    try {
      InputStream is = yamlResource.getInputStream();
      Properties properties = new Properties();
      load(properties, is);
      return properties;
    } catch ( Exception e ) {
      throw new IllegalStateException("Can't get PropertySource from YAML resource=" + yamlResource,e);
    }
  }

  private static void load(Properties props, InputStream is) throws IOException {
    load(props, new InputStreamReader(is));
  }

  private static void load(Properties props, Reader reader) throws IOException {
    Yaml yaml = instanceOfYaml();
    @SuppressWarnings("unchecked")
    Map<String, Object> map = (Map<String, Object>) yaml.load(reader);
    // now we can populate supplied props
    assignProperties(props, map, null);
  }

  private static void assignProperties(Properties props, Map<String, Object> map, String path) {
    for (Entry<String, Object> entry : map.entrySet()) {
      String key = entry.getKey();
      if ( StringUtils.hasLength(path) )
        key = path + "." + key;
      Object val = entry.getValue();
      if (val instanceof String) {
        // see if we need to create a compound key
        props.put(key, val);
      } else if (val instanceof Map) {
        assignProperties(props, (Map<String, Object>) val, key);
      }
    }
  }

  public static Yaml instanceOfYaml() {
    DumperOptions options = new DumperOptions();
    options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
    final Yaml yaml = new Yaml(new Loader(), new Dumper(options), new Resolver() {
      /**
       * @see org.yaml.snakeyaml.resolver.Resolver#addImplicitResolvers()
       */
      @Override
      protected void addImplicitResolvers() {
        addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
        // disable resolving of floats and integers
        // addImplicitResolver(Tags.FLOAT, FLOAT, "-+0123456789.");
        // addImplicitResolver(Tag.INT, INT, "-+0123456789");
        addImplicitResolver(Tag.MERGE, MERGE, "<");
        addImplicitResolver(Tag.NULL, NULL, "~nN\0");
        addImplicitResolver(Tag.NULL, EMPTY, null);
        addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
        addImplicitResolver(Tag.VALUE, VALUE, "=");
      }
    });
    return yaml;
  }

}

Обратите внимание, что это также вдохновляет ResourcePropertySource и загружает свойства в кодировке ISO 8859-1. Я открыл для этого ошибку: SPR-10096

Вы можете добавить этот источник собственности в контекст вашего приложения. Это также может быть сделано в модульных тестах:

public class PropertySourceContextLoader extends GenericXmlContextLoader {
    @Override
    protected void loadBeanDefinitions(GenericApplicationContext context,MergedContextConfiguration mergedConfig) {
        PropertySource<String> ps = new MyPropertySource();
        context.getEnvironment().getPropertySources().addLast(ps);
        super.loadBeanDefinitions(context, mergedConfig);
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = PropertySourceContextLoader.class, locations = { "classpath:/spring-application-context.xml" })
public class SpringBasedTest {
         ..........
}

3



Для полных и полных нубов, таких как я, у которых есть нулевое знание того, что автор на самом деле делает, но нужно делать это в любом случае ... вот как я заработал. Не знаю, как де-осудить instanceOfYaml (). Еще одна вещь, мой проект Spring Boot Eclipse, прочитанный из файлов с пометкой .yml, а не .yaml

import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Dumper;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.resolver.Resolver;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

public class YamlPropertiesPersister implements PropertiesPersister {
    @Override
    public void load(Properties props, InputStream is) throws IOException {
        load(props, new InputStreamReader(is));
    }

    /**
     * We want to traverse map representing Yaml object and each time we will find String:String value pair we want to
     * save it as Property. As we are going deeper into map we generate a compound key as path-like String
     *
     * @param props
     * @param reader
     * @throws IOException
     * @see org.springframework.util.PropertiesPersister#load(java.util.Properties, java.io.Reader)
     */
    @Override
    public void load(Properties props, Reader reader) throws IOException {
        Yaml yaml = instanceOfYaml();
        Map<String, Object> map = (Map<String, Object>) yaml.load(reader);
        // now we can populate supplied props
        assignProperties(props, map, null);
    }

    /**
     * @param props
     * @param map
     */
    public void assignProperties(Properties props, Map<String, Object> map, String path) {
        for (Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            if (!StringUtils.isEmpty(path))
                key = path + "." + key;
            Object val = entry.getValue();
            if (val instanceof String) {
                // see if we need to create a compound key
                props.put(key, val);
            } else if (val instanceof Map) {
                assignProperties(props, (Map<String, Object>) val, key);
            }
        }
    }

    @Override
    public void store(Properties props, OutputStream os, String header) throws IOException {
        throw new UnsupportedOperationException("Current implementation is a read-only");
    }

    @Override
    public void store(Properties props, Writer writer, String header) throws IOException {
        throw new UnsupportedOperationException("Current implementation is a read-only");
    }

    @Override
    public void loadFromXml(Properties props, InputStream is) throws IOException {
        throw new UnsupportedOperationException("Use DefaultPropertiesPersister if you want to read/write XML");
    }

    @Override
    public void storeToXml(Properties props, OutputStream os, String header) throws IOException {
        throw new UnsupportedOperationException("Use DefaultPropertiesPersister if you want to load/store to XML");
    }

    @Override
    public void storeToXml(Properties props, OutputStream os, String header, String encoding) throws IOException {
        throw new UnsupportedOperationException("Use DefaultPropertiesPersister if you want to read/write XML");
    }


    public static Yaml instanceOfYaml() {
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
        final Yaml yaml = new Yaml(new Loader(), new Dumper(options), new Resolver() {
            /**
             * @see org.yaml.snakeyaml.resolver.Resolver#addImplicitResolvers()
             */
            @Override
            protected void addImplicitResolvers() {
                addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
                // disable resolving of floats and integers
                // addImplicitResolver(Tags.FLOAT, FLOAT, "-+0123456789.");
                // addImplicitResolver(Tag.INT, INT, "-+0123456789");
                addImplicitResolver(Tag.MERGE, MERGE, "<");
                addImplicitResolver(Tag.NULL, NULL, "~nN\0");
                addImplicitResolver(Tag.NULL, EMPTY, null);
                addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
                addImplicitResolver(Tag.VALUE, VALUE, "=");
            }
        });
        return yaml;
    }
}

1



Сообщение форума, на которое вы ссылаетесь, было на форуме dmServer, а не на Spring Framework, и между ними очень мало отношений, поэтому я ничего не прочитал бы в нем.

Кроме того, YAML почти не слышно в мире Java, поэтому добавление поддержки для него было бы жестом жетона (если вы помилуете выражение) в лучшем случае. XML доминирует на Java, особенно на стороне сервера, поэтому мало что нужно для плавания против прилива, особенно для формата меньшинства, такого как YAML.

Сказав это, написали собственные YamlPropertyPlaceholderConfigurer должно быть достаточно простым, предполагая, что вы можете найти парсер YAML для Java.


0