Динамически заполнять строки из файла свойств в Java - PullRequest
2 голосов
/ 01 сентября 2011

Кто-нибудь знает способ загрузки файла свойств и динамического создания строк с идентичными именами значения ключа?

Я пытаюсь очистить свой код, переместив все системные сообщения и т. Д. Из логики в файл свойств, но хочу избежать необходимости иметь класс, состоящий из десятков строк, таких как:

final String COMMS_ERROR = properties.getProperty(COMMS_ERROR);

Пример того, чего я пытаюсь достичь:

for (String key : properties.getPropertyValues()) {
    final String <key> = properties.getProperty(key)
}

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

Единственное решение, о котором я подумал, - это заполнить HashMap ключами / значениями из файла свойств, но тогда это будет означать менее элегантный код в виде:

import com.x.y.messages;
...
    throw new Exception(HM.get("COMMS_ERROR"));

Где HM - это HashMap, расположенный в com.x.y.messages ...

В идеале я просто хочу уметь:

import com.x.y.messages;
....
    throw new Exception(COMMS_ERROR);

Любые мысли / советы приветствуются.

Ответы [ 6 ]

3 голосов
/ 01 сентября 2011

Если эти свойства могут измениться после компиляции (если нет, то зачем их использовать), у вас не будет возможности создавать И использовать эти строки динамически. Конечно, есть способы динамически создавать код (например, ткачество во время выполнения AOP), но этот код не будет использоваться в обычном процессе компиляции.

Так как бы компилятор узнал, что COMMS_ERROR действительно существует в этой строке throw new Exception(COMMS_ERROR);? Это не может и, следовательно, вам нужно пойти на HashMap подход. Обратите внимание, что Properties на самом деле Map<String, String> (хорошо, это Hashtable<Object, Object> в Java 6, но он действует как Map<String, String>), поэтому нет необходимости создавать новый.

Редактировать: то, что вы можете сделать, это использовать статический импорт следующим образом:

package yourpackage;

public class Props
{
  private static Properties props;

  public static String prop(String prop)
  {
    return props.getProperty( prop );
  }
}

Используйте это так:

import static yourpackage.Props.prop;

....

prop("someKey");

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

1 голос
/ 01 сентября 2011

Что не так с

        Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources( "com/x/y/message.properties" );

        while( resources.hasMoreElements() ) {
            URL url = resources.nextElement();
            Properties p = new Properties();
            p.load( url.openStream() );
            ...
        }

Я не понимаю, зачем хранить данные от Properties до HashMap

import com.x.y.messages;
    ....
throw new Exception(p.getProperty("COMMS_ERROR"));
0 голосов
/ 27 января 2013

Это выглядит почти точно, что делает моя библиотека! Проверьте это: http://owner.aeonbits.org

Пример:

# Properties file (call it the same name as the Java class and put 
# it in the same package
port=80
hostname=foobar.com
maxThreads=100

//properties mapper interface
public interface ServerConfig extends Config {
    int port();
    String hostname();
    int maxThreads();
}

// how you use it:

ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
System.out.println("Server " + cfg.hostname() + ":" + cfg.port() + " will run " + cfg.maxThreads());

Но вы можете сделать гораздо больше с библиотекой OWNER.

0 голосов
/ 01 сентября 2011

Ранее я сам писал вспомогательные классы, которые синхронизировали файл свойств с классом констант.Но это работает, только если вы придерживаетесь соглашений.

Допустим, у вас есть такой класс:

public final class Constants{

    private Constants(){}
    public static final String SOME_PROPERTY_NAME = "some.property.name";
    public static final String THIS_ONE_NOT_SET_YET = null;
    public static final String PROPERTY_NOT_DEFINED = "property.not.defined";

}

и файл свойств, подобный этому:

some.property.name=Hello World
no.constant.for.this.yet=Hello again

Мой вспомогательный класс должен был бы перебрать все свойства и все константы, сопоставить и идентифицировать те, которые ни к чему не относились.

Так что в этом случае:

a)

В Constants.java

public static final String THIS_ONE_NOT_SET_YET = null;

будет изменен на

public static final String THIS_ONE_NOT_SET_YET = "this.one.not.set.yet";

, а в файле свойств будет введена эта строка:

this.one.not.set.yet=

b)

в файле свойств, эта строка будет добавлена ​​

property.not.defined=

c)

В Constants.java эта строка будет добавлена:

public static final String NO_CONSTANT_FOR_THIS_YET = "no.constant.for.this.yet";

Это не идеально, но таким образом вы получаете безопасность псевдокомпиляции.Вы компилируете с использованием констант, и ваш помощник поддерживает синхронизацию этих констант со свойствами.

Очевидно, что этот подход становится намного сложнее, если у вас есть более сложные сценарии.Например,

  • Свойства, начинающиеся с "foo".хранится в «foo.properties», а свойства называются «bar».хранятся в "bar.properties"
  • Интернационализация: теперь у вас есть foo.properties, foo.properties.es, foo.properties.de и т. д. Поддерживать синхронизацию - это большая неприятность.

Возможно, нужно учесть, что класс констант должен динамически создаваться из одного или нескольких файлов свойств в процессе сборки.Ваш генератор кода (основной класс, скрипт Groovy или даже скрипт оболочки) должен был бы просто сделать это (псевдокод):

properties = readProperties()
writeClassHeader()
for prop : properties
    writeln "public static final String " 
            + prop.name.upperCase().replace(".","_") + "= \"" + prop.name + "\";"
writeClassFooter()
0 голосов
/ 01 сентября 2011

Мне не известен инструмент, который бы это делал, и он не подходит для обычного Java-подхода. (В Java вы не можете добавлять новые переменные на лету ... в отличие от Javascript, например.)

Теоретически возможно реализовать что-то в этом духе, но это, вероятно, повлечет за собой генерацию и компиляцию класса для каждого типа файла свойств и перекомпиляцию остальной части вашего кода с API этих классов. Если у вас нет огромного количества этих файлов свойств, проще кодировать классы вручную. (И если у вас есть огромное количество этих файлов свойств, я бы хотел узнать, есть ли лучший способ обработки информации в этих файлах.)


Да, это то, на что я надеялся - библиотека, которая содержала бы необходимую магию

К сожалению, ни одна обычная библиотека не смогла бы сделать это. Генерация / перекомпиляция должна происходить во время сборки. (Библиотека может генерировать классы во время выполнения и даже компилировать и загружать их. Но заставить ее перекомпилировать остальную часть вашего приложения во время выполнения в лучшем случае сложно, и, как правило, невозможно ..., поскольку исходный код недоступен.)

0 голосов
/ 01 сентября 2011

Вы не можете объявлять локальные переменные на лету, но вы можете использовать карту:

Map<String, String> messages = new HashMap<String, String>();

for (String key : properties.getPropertyValues()) {
    messages.put(key, properties.getProperty(key));
}

, чтобы использовать их:

throw new Exception( messages.get( "KEY" ) )

См. http://download.oracle.com/javase/6/docs/api/java/util/Map.html

Но на самом деле, как отметил Томас выше, вам не нужен новый HashMap, просто

throw new Exception( properties.getProperties(key) );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...