Чтение конфигурационных файлов в Java: nProperty

    image
    Многие разработчики сталкиваются с необходимостью чтения конфигурационных (*.ini, *.prop, *.conf, etc.) файлов в разрабатываемых приложениях. В Java есть стандартный класс Properties, с помощью которого можно очень легко загрузить ini-файл и прочитать его свойства. При большом объеме конфигурационных файлов чтение и запись настроек в объекты превращается в очень нудную и рутинную работу: создать объект Properties, конвертировать каждую настройку в нужный формат и записать его в поле.

    Библиотека nProperty (Annotated Property) призвана упростить этот процесс, сократив примерно в два раза требуемый код для написания загрузчиков настроек.

    Чтобы показать, каким образом возможно обещанное сокращение кода в два раза, ниже приведены два примера: в первом примере используется стандартный класс Properties, во-втором — nProperty.


    Статья и сама библиотека nProperty написана моим другом и товарищем по цеху Yorie для внутрикомандных повседневных нужд, и так как он, к сожалению, не имеет в данный момент инвайта на хабре, я взял на себя смелость, с его согласия, опубликовать сие творение для «хабровских» масс.

    Содержание


    1. Просто о главном
    2. Чтение примитивных и стандартных типов
    3. Десериализация в массивы и коллекции
    4. Десериализация в пользовательские типы
    5. Модификаторы уровней доступа
    6. Инициализация всех членов класса
    7. Значения по умолчанию
    8. Переопределение имен
    9. Работа с не статичными полями классов
    10. Использование методов
    11. Обработка событий
    12. Использование потоков и дескрипторов файлов
    13. Замечания
    14. Лицензия
    15. Ссылки



    Просто о главном


    В обоих примерах будет использован один и тот же файл конфигурации:
    SOME_INT_VALUE = 2
    SOME_DOUBLE_VALUE = 1.2
    SOME_STRING_VALUE = foo
    SOME_INT_ARRAY = 1;2;3
    


    Пример №1. Загрузка конфигурации с помощью стандартного класса Properties.
    public class Example1
    {
    	private static int SOME_INT_VALUE = 1;
    	private static String SOME_STRING_VALUE;
    	private static int[] SOME_INT_ARRAY;
    	private static double SOME_DOUBLE_VALUE;
    
    	public Example1() throws IOException
    	{
    		Properties props = new Properties();
    		props.load(new FileInputStream(new File("config/example.ini")));
    
    		SOME_INT_VALUE = Integer.valueOf(props.getProperty("SOME_INT_VALUE", "1"));
    		SOME_STRING_VALUE = props.getProperty("SOME_STRING_VALUE");
    		SOME_DOUBLE_VALUE = Double.valueOf(props.getProperty("SOME_DOUBLE_VALUE", "1.0"));
    
    		// Предположим, что в настройках находится список целых через точку с запятой
    		String[] parts = props.getProperty("SOME_INT_ARRAY").split(";");
    		SOME_INT_ARRAY = new int[parts.length];
    		for (int i = 0; i < parts.length; ++i)
    		{
    			SOME_INT_ARRAY[i] = Integer.valueOf(parts[i]);
    		}
    	}
    
    	public static void main(String[] args) throws IOException
    	{
    		new Example1();
    	}
    }
    


    Пример №2. Загрузка конфигурации с помощью nProperty.
    @Cfg
    public class Example2
    {
    	private static int SOME_INT_VALUE = 1;
    	private static String SOME_STRING_VALUE;
    	private static int[] SOME_INT_ARRAY;
    	private static double SOME_DOUBLE_VALUE;
    
    	public Example2() throws NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, InvocationTargetException
    	{
    		ConfigParser.parse(Example2.class, "config/example.ini");
    	}
    
    	public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IOException, IllegalAccessException
    	{
    		new Example2();
    	}
    }
    

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


    Чтение примитивных и стандартных типов


    Во втором вышеприведенном примере стоит обратить внимание на аннотацию @Сfg. Она и является причиной сократившегося кода. Библиотека nProperty основана на аннотациях, которые могут быть применены к классам, полям и методам классов.

    Чтобы прочитать из конфигурационного файла настройки, тип которых относится к примитивным, достаточно каждое поле класса обозначить аннотацией @Сfg:

    public class Example3
    {
        @Cfg
        private static int SOME_INT_VALUE;
    
        @Cfg
        private static short SOME_SHORT_VALUE;
    
        @Cfg
        private static long SOME_LONG_VALUE;
    
        @Cfg
        private static Double SOME_DOUBLE_VALUE;
    
        /* ... */
    }
    

    Библиотека nProperty поддерживает достаточно богатый набор стандартных типов:
    • Integer/int
    • Short/short
    • Double/double
    • Long/long
    • Boolean/boolean
    • String
    • Character/char
    • Byte/byte
    • AtomicInteger, AtomicLong, AtomicBoolean
    • BigInteger, BigDecimal

    Все эти перечисленные типы могут быть использованы в примере выше.


    Десериализация в массивы и коллекции


    Помимо стандартных типов также возможна десериализация в массивы с одним условием — тип массива должен принадлежать множеству стандартных типов:

    /*
        В файле конфигурации находится следующее:
            SOME_INT_ARRAY = 1--2--3
            SOME_SHORT_ARRAY = 3>2<1
            SOME_BIGINTEGER_ARRAY = 1;2;3
     */
    public class Example5
    {
        @Cfg(splitter = "--")
        private static int[] SOME_INT_ARRAY;
        @Cfg(splitter = "[><]")
        private static short[] SOME_SHORT_ARRAY;
        @Cfg
        private static BigInteger[] SOME_BIGINTEGER_ARRAY;
    }
    

    В случае с массивами библиотека сама позаботится о том, чтобы проинициализировать массив нужного размера.

    Обратите внимание на аннотации у SOME_INT_ARRAY и SOME_SHORT_ARRAY. По умолчанию nProperty использует в качестве разделителя символ ";". Его можно легко переопределить, указав в аннотации к полю свойство splitter. И, как можно заметить, разделителем может выступать полноценное регулярное выражение.

    Помимо массивов возможно использование коллекций, а именно — списков. Здесь необходимым является одно условие — коллекция должна быть обязательно проинициализирована до запуска чтения конфигурации. Это связано с тем, что экземпляры объектов коллекций могут быть разными (ArrayList, LinkedList и т.д.):

    public class Example6
    {
        @Cfg
        private static List<Integer> SOME_ARRAYLIST_COLLECTION = new ArrayList<>();
    
        @Cfg
        private static List<Integer> SOME_LINKEDLIST_COLLECTION = new LinkedList<>();
    }
    

    В остальном для коллекций сохраняются все свойства десериализации массивов.


    Десериализация в пользовательские типы


    В качестве дополнительной функции библиотека может работать с пользовательскими классами. Пользовательский тип обязательно должен иметь конструктор: MyClass(String), в противном случае будет вызвано исключение. Уровень видимости конструктора не имеет значения, он может быть как public, так и private:

    public class Example8
    {
        private static class T
        {
            private final String value;
    
            private T(String value)
            {
                this.value = value;
            }
    
            public String getValue() { return value; }
        }
    
        @Cfg
        private static T CUSTOM_CLASS_VALUE;
    }
    

    Как видите, библиотеке все равно, что нужный конструктор обозначен модификатором private. В результате в поле value класса T будет записано значение из файла конфигурации.


    Модификаторы уровней доступа


    Стоит отметить, что библиотеке nProperty абсолютно все равно, какие модификаторы доступа имеет поле, метод или конструктор — библиотека работает через механизм Reflections и управляет этими модификаторами самостоятельно. Конечно же, вмешательство в модификаторы никак не коснется других частей приложения, к которым библиотека отношения не имеет.


    Инициализация всех членов класса


    В предыдущих примерах видно, что при большом количестве полей в конфигурации придется написать большое кол-во аннотаций @Сfg. Чтобы избежать этой рутинной работы nProperty позволяет добавить аннотацию к самому классу, тем самым обозначив все поля класса как потенциальные поля для записи в них настроек из файла конфигурации:

    @Cfg
    public class Example7
    {
        /* Все поля класса будут использованы как поля для чтения настроек */
        private static int SOME_INT_VALUE = 1;
        private static String SOME_STRING_VALUE;
        private static int[] SOME_INT_ARRAY;
        private static double SOME_DOUBLE_VALUE;
        private static List<Integer> SOME_ARRAYLIST_COLLECTION = new ArrayList<>();
        private static List<Integer> SOME_LINKEDLIST_COLLECTION = new LinkedList<>();
    
        @Cfg(ignore = true)
        private final static Logger log = Logger.getAnonymousLogger();
    }
    

    Здесь стоит обратить внимание на член класса log. Ему назначена аннотация @Сfg с включенным свойством ignore. Это свойство означает, что данное поле не будет использоваться библиотекой при чтении конфигурации, а попросту будет пропущено. Данное свойство следует использовать только в случае, когда аннотация действует на весь класс, как показано в примере выше.


    Значения по умолчанию


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

    /* Файл конфигурации не содержит свойства WRONG_PROPERTY */
    @Cfg
    public class Example9
    {
        private int WRONG_PROPERTY = 9000;
    
        private int SOME_INT_VALUE;
    }
    

    В данном случае после парсинга конфигурации в поле WRONG_PROPERTY будет храниться все то же значение 9000.


    Переопределение имен


    В случаях, когда имя поля класса не совпадает с именем конфигурации в конфигурационном файле, его можно принудительно переопределить:

    public class Example10
    {
        @Cfg("SOME_INT_VALUE")
        private int myIntValue;
    }
    

    Естественно, если есть возможность сохранять равнозначность имен в коде и в файлах конфигурации, то лучше так и делать — это избавит от необходимости аннотировать каждое поле класса.


    Работа с не статичными полями классов


    Библиотека способна работать как с классами, так и с их экземплярами. Это определяется путем различных вызовов метода ConfigParser.parse():

    @Cfg
    public class Example11
    {
        private static int SOME_SHORT_VALUE;
    
        private int SOME_INT_VALUE;
    
        public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, InvocationTargetException
        {
            ConfigParser.parse(Example11.class, "config/example.ini"); // В данном вызове не будет использоваться переменная SOME_INT_VALUE
    
            ConfigParser.parse(new Example11(), "config/example.ini");
        }
    }
    

    Как видно, в примере использованы два разных вызова одного и того же метода. После отработки метода ConfigParser.parse(Example11.class, «config/example.ini») в SOME_INT_VALUE будет нуль, причем это совершенно не зависит от файла конфигурации, потому что данное поле не является статичным и не может быть использовано без экземпляра объекта.

    Сразу после второго вызова ConfigParser.parse(new Example11(), «config/example.ini») поле SOME_INT_VALUE для созданного объекта примет значение в соответствии с содержанием файла конфигурации.

    Следует аккуратно пользоваться этой возможностью библиотеки, так как могут появиться ситуации, когда конфигурация не будет прогружаться по «непонятной» причине, а на самом деле окажется, что просто не был проставлен модификатор static.


    Использование методов


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

    Существует три решения в таких ситуациях:
    1. самостоятельно проверить или изменить значение после того, как библиотека проанализирует файл настроек и заполнит все поля класса
    2. создать в качестве типа свой класс-обертку с конструктором (как было показано выше)
    3. исключить поле класса из списка свойств и назначить его методу


    Самый удобный и корректный способ — №3. Библиотека nProperty позволяет работать не только с полями, но и с методами:
    public class Example12
    {
        private static List<Integer> VALUE_CHECK = new ArrayList<>();
    
        @Cfg("SOME_INT_ARRAY")
        private void checkIntArray(String value)
        {
            String[] values = value.split("--");
    
            for (String val : values)
            {
                try
                {
                    /* ограничим значение промежутком [0,100] */
                    VALUE_CHECK.add(Math.max(0, Math.min(100, Integer.parseInt(val))));
                }
                catch (Exception ignored) {}
            }
        }
    }
    

    Здесь в метод checkIntArray(String) в качестве первого параметра будет передано значение SOME_INT_ARRAY из файла конфигурации. Это очень удобный механизм для случаев, когда стандартные решения библиотеки не подходят. В методе-обработчике можно делать все, что угодно.

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

    Как и прежде поддерживается преобразование типов, если тип первого параметра метода отличен от String.

    Как и с полями класса, если имя метода эквивалентно имени настройки в файле конфигурации, то можно опустить задание имени в аннотации.


    Обработка событий


    Библиотека nProperty позволяет обрабатывать некоторые события во время чтения конфигурации. Для того, чтобы реализовать обработку событий, необходимо реализовать интерфейс IPropertyListener и все его методы. Вызов событий возможен только в случае работы с полноценными объектами, экземплярами класса, реализующего интерфейс IPropertyListener.

    Поддерживаемые события:
    • onStart(String path) — отправляется перед началом загрузки файла конфигурации
    • onPropertyMiss(String name) — вызывается в случае, если некоторая именованная конфигурация не была найдена в файле настроек, но была обозначена в классе аннотацией @Сfg
    • onDone(String path) — вызывается при завершении загрузки файла конфигурации
    • onInvalidPropertyCast(String name, String value) — вызывается в случае, когда удалось прочитать значение настройки из файла конфигурации, но не удалось привести это значение к типу соответствующего поля класса


    @Cfg
    public class Example13 implements IPropertyListener
    {
        public int SOME_INT_VALUE;
        public int SOME_MISSED_VALUE;
        public int SOME_INT_ARRAY;
    
        @Override
        public void onStart(String path)
        {
    
        }
    
        @Override
        public void onPropertyMiss(String name)
        {
    
        }
    
        @Override
        public void onDone(String path)
        {
    
        }
    
        @Override
        public void onInvalidPropertyCast(String name, String value)
        {
    
        }
    
        public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, InvocationTargetException
        {
            ConfigParser.parse(new Example13(), "config/example.ini");
        }
    }
    

    В приведенном примере будут вызваны все 4 события. Событие onPropertyMiss будет вызвано из-за поля SOME_MISSED_VALUE, которое отсутствует в файле конфигурации. Событие onInvalidPropertyCast будет вызвано из-за неверного типа поля SOME_INT_ARRAY.


    Использование потоков и дескрипторов файлов


    Библиотека умеет принимать на вход не только имена файлов, также возможна передача объекта java.io.File, или потока данных, производного от абстрактного класса java.io.InputStream:

    @Cfg
    public class Example14
    {
        public int SOME_INT_VALUE;
        public int SOME_MISSED_VALUE;
        public int SOME_INT_ARRAY;
    
        public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, InvocationTargetException
        {
            ConfigParser.parse(new Example14(), "config/example.ini");
            ConfigParser.parse(new Example14(), new File("config", "example.ini"));
            ConfigParser.parse(new Example14(), new FileInputStream("config/example.ini"), "config/example.ini");
        }
    }
    

    Как видно, в приведенном примере в случае работы с потоком, библиотека требует дополнительно указать название конфигурации, так как невозможно его получить из низкоуровневого объекта FileInputStream. Название не является важной частью и будет использовано библиотекой для отображения информации (в том числе, при работе с событиями).

    Таким образом, данные могут быть получены не только из файловой системы, но и от любого источника данных, работающего по стандартам Java. Умение работать с java.io.InputStream дает возможность библиотеке быть успешно примененной в операционных системах Android:
    @Cfg
    public class ConfigGeneral extends PropertyListenerImpl
    {
    	public static String SERVER_IP;
    	public static int SERVER_PORT;
    
    	private ConfigGeneral()
    	{
    		String path = "config/network/general.ini";
    		try
    		{
    			InputStream input = getApplicationContext().getResources().getAssets().open(path);
    			ConfigParser.parse(this, input, path);
    		}
    		catch(Exception e)
    		{
    			Log.e(TAG, "Failed to Load " + path + " File.", e);
    		}
    	}
    
    	public static void loadConfig()
    	{
    		new ConfigGeneral();
    	}
    }
    



    Замечания


    В связи с не очень прозрачной работой SecurityManager'a библиотека имеет ограничение на тип задаваемого поля конфигуратора: поле не должно иметь модификатора final.


    Лицензия


    Библиотека распространяется по лицензии Apache License v.2.0


    Ссылки


    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Будем использовать? :)

    Поделиться публикацией

    Комментарии 53

      0
      Когда-то еще давно сам реализовал для своего проекта подобную систему
      Скриншот

      И к слову о недостатках, разве при помощи рефлексии нельзя изменить значение финализированного поля?

         static void setFinalStatic(Field field, Object newValue) throws Exception {
            field.setAccessible(true);
      
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
      
            field.set(null, newValue);
         }
      
        +1
        Если прочитать подробнее о таких методах (например, в первоисточнике приведенного хака: stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection), то можно сделать вывод, что такой подход может работать нестабильно, в частности из-за SecurityManager, не говоря уже о Android.
        Библиотеке этого не нужно.
          0
          Ооо, не обратил внимания на автора :) Привет-привет) Но я бы и не относил это к недостаткам, эти переменные и незачем финализировать.
            0
            Привет-привет) Спасибо за замечание — подправил.
            +7
            Основная причина вовсе не security manager — final-поля (и особенно static final) очень агрессивно инлайнятся. И, согласно спецификации, компилятор вовсе не обязан следить за их последующими изменениями. То есть даже если у вас получится поле изменить — вы не можете быть уверенным, что ваш код будет видеть новое значение
          0
          Библиотека интересная. А исходники будут?
            0
            Думаю да, если в этом есть необходимость. Так же постараемся сделан залив в maven репозиторий.
              0
              Ждем maven, А там будем посмотреть :)
              Для использования не только в домашних поделках исходники лучше закинуть на github, а артифакты — в центральный репозиторий. Что бы была гарантия сборки проекта через год и через два. Даже при потере интереса авторами.
              +1
              Библиотека и ее исходники обосновалась тут: code.google.com/p/jfork/
              +14
              Библиотека должна быть представлена в Maven, иначе никакие промо статьи ей не помогут.
              И, конечно, исходник должен быть в паблике.

              Это самые верные доказательства, что библиотека готова к реальным проектам.
                0
                Я не буду, потому что уже использую OWNER :)
                  +1
                  Спасибо за ссылку! Судя по описанию, действительно очень мощный фреймворк конфигурации, пока что самое мощное и удобное решение из всех, что я видел.

                  P.S. думаю вот эта ссылка лучше покажет все его преимущества: owner.aeonbits.org/docs/usage/
                  0
                  Советую всем Merlin Hughes Config Framework
                    0
                    Несколько из другой степи этот фреймворк + тянуть 3 доп. библы для его работы это не айс.
                      +1
                      В больших Java приложениях обычно уже используются библиотеки apache-commons и ничего дополнительного не нужно.
                        0
                        Судя по приведенной ссылке, не только commons
                        image

                        Так или иначе, я никому ничего не навязываю — естественно, что каждый будет использовать то, к чему привык.
                          0
                          Все приведенные выше библиотеки принадлежат к apache-commons.
                            0
                            Тем не менее, это три физически отдельных библиотеки
                              0
                              Вы написали:
                              Судя по приведенной ссылке, не только commons

                              Вам ответили, что все они принадлежат к apache-commons (или являются apache-commons, что по сути одно, т. к. apache-commons — собирательное название группы библиотек от Apache Software Foundation).

                              Id est ваше утверждение неверно. Там только commons.

                              Вы пишите дальше:
                              Тем не менее, это три физически отдельных библиотеки

                              И? О чём вы спорите? Никто не отрицал, что это три библиотеки.

                              Вы начали спорить не с тем, что там не три библиотеки, а с тем что они не входят в apache-commons. Не меняйте тезисы по ходу спора.
                    +3
                    Не вижу больших преимуществ над .xml + JAXB (XStream, etc).

                    Кода будет столько же (аннотации + пару строк вызова маршала), а вот такие плюшки как schema (для валидации формата без запуска), xslt (конвертирование старых конфигов, когда формат меняется; отображение конфига в более привлекательнои виде для менеджеров), xpath (полноценые запросы по конфигу) делают XML — куда более интересным вариантом для больших конфигов (для мальеньких и Properties хватит).
                      0
                      По скольку в таких фишках не было надобности — их, пока, нет.
                        0
                        XML out-of-the-box предлогает все тоже, что и nProperties и даже больше. В чем полезность этой библиотеки?
                          0
                          Например, в lazy-инициализации полей конфигуратора.
                        +2
                        +1
                        Или JSON (если в проекте используется то и для конфига можно)
                          +1
                          В дополнение,
                          Json почти такой же:
                          Списки, массивы. В том числе и из объектов.
                            –2
                            Кому нужно засилие скобок в конфигах, которые собственно всем своим нутром должны быть как можно более читабельны для человека? Где Вы вообще видели, чтобы конфигурацию для приложения хранили в JSON?
                            Еще раз повторяюсь — данная библиотека расчитана на замену стандартного Properties Java — никакие XML, JSON файлы приводить тут вообще ни к месту. Это лишь альтернатива, которую мы успешно используем в своих проектах. Как там говорили? «Не нравится — не ешь» ©
                              +1
                              Json — это нечто среднее между бинарным форматом и текстовым, что вполне удобно для использования.
                              Если вы спрашиваете о программах c конфигами на нём— то это например новый minecraft лаунчер.
                              P.S. я не имею ничего против вашего конфига, выглядит вполне рабочим.
                              Я вообще люблю библиотеки делающие рутину за меня. :)
                        +2
                        Мне нравится typesafe/config. Там есть наследование конфигов.
                          –2
                          Это Scala библиотека. Мы говорим о Java.
                            +2
                            > Configuration library for JVM languages.

                            > implemented in plain Java with no dependencies
                              +2
                              В самом деле, извиняюсь. Поставили в тупик билд файлы scala.
                          +2
                          По мотивам habrahabr.ru/post/113145/#comment_3630589

                          Каждый программист за свою жизнь должен написать тетрис, систему логирования и библиотеку конфигов :)
                            0
                            Использую свою обвеску к apache commons-configuration для использования в IoC-контейнере.

                            Аннотация следующего вида:
                            import javax.enterprise.util.Nonbinding;
                            import javax.inject.Qualifier;
                            import java.lang.annotation.Documented;
                            import java.lang.annotation.Inherited;
                            import java.lang.annotation.Retention;
                            import java.lang.annotation.Target;
                            
                            import static java.lang.annotation.ElementType.*;
                            import static java.lang.annotation.RetentionPolicy.RUNTIME;
                            
                            @Target({FIELD, PARAMETER, METHOD})
                            @Retention(RUNTIME)
                            @Qualifier
                            @Inherited
                            @Documented
                            public @interface Configuration {
                              @Nonbinding String value();
                              @Nonbinding String config() default "default";
                              @Nonbinding boolean secret() default false;
                              @Nonbinding String defaultValue() default "";
                            }
                            


                            Позволяет указать, какое поле читать, из какого конфига (если их несколько), писать ли его значение в отладочном логе и значение по умолчанию.
                              0
                              Как это работает? Расскажите подробнее пожалуйста.
                                0
                                Данное решение заточено под CDI (JSR-299).

                                В managed bean:
                                @ApplicationScoped
                                class SomeBean {
                                  @Inject
                                  @Configuration(value = "some.property.name", defaultValue = "10")
                                  private int propertyName;
                                }
                                


                                Тогда IoC-framework ищет источник для инжекции, который описан в библиотеке:
                                class ConfigurationInjector {
                                // ...
                                  @Produces
                                  @Configuration("")
                                  public Integer integerConfig(InjectionPoint point) {
                                    Configuration configAnnotation = point.getAnnotated().getAnnotation(Configuration.class);
                                    // вытаскиваем configAnnotation.value и используем в кач-ве ключа для поиска в конфигурационных файлах
                                  }
                                // ...
                                }
                                

                                где из аннотации вытаскивается имя ключа, значение по умолчанию, имя конфига. С помощью commons-configuration происходит вытаскивание нужного из конфига (с учетом подстановок и include-деклараций) и дальше через Reflections API запись в аннотированное поле.
                              +2
                              IPropertyListener
                              

                              Меня одного напрягает, когда в Java добавляют «I» к имени интерфейса?..
                                –3
                                Как бы это — общепринятая практика.
                                  +2
                                  Это не является общепринятой практикой: зависит от конвенции.

                                  Часто напоминает iNumbers, sName и прочие прелести «венгерской» нотации в Java.

                                  Возможно, что это наследие C++ (особенно, под win у них это чаще проскакивает).
                                  +1
                                  В C++, наверное, да — общепринятая. В Java интерфейсы, обычно, отличаются окончанием -able, либо вообще ни чем не отличаются. Впрочем, это и не нужно.
                                    0
                                    Перенес ниже.
                                      +1
                                      В наше время важнее семантика — типизацию давно берет на себя IDE.
                                      Поэтому от префиксов типов избавляются.
                                    0
                                    Вы уж извините, что я все критикую, однако…
                                    Разработка библиотеки не преследовала коммерческих целей и преследовать не будет.

                                    Вряд ли эту строку можно считать лицензионным соглашением. Если вы действительно хотите, чтобы Вашу библиотеку могли легально использовать, есть смысл задуматься над указанием полноценной лицензии, которая разграничивает сферы ответственности и явно указывать все ограничения, сопряженные с использованием Вашего ПО.
                                      0
                                      Спасибо за замечание.
                                      Прописали лицензию Apache 2.0
                                      0
                                      Фиг знает. Обычно у меня куча конфигурируемых компонентов, но всего один конфигурационный файл на все, где свойства для каждого компонента прописываются с неким префиксом. Неплохо бы уметь вытаскивать только эти свойства из одного и того же файла в зависимости от указанного префикса: Cfg(prefix=«db»)…

                                      db.driver=aaa
                                      db.url=bbb
                                      db.user=XXX
                                      db.password=YYY

                                      ws.url=…
                                      ws.user=…
                                      ws.passwd=…

                                      component1.prop1=…
                                      component1.prop2=…
                                        0
                                        Спасибо, будет добавлено в новой версии ) Пока готовим залив на maven
                                        0
                                        Всем спасибо за комментарии.

                                        Библиотека и ее исходники опубликованы на Google Code под лицензией Apache 2.0
                                        code.google.com/p/jfork/

                                        Залив в Maven будет сразу же, как появится свободное для этого действа время :)
                                          0
                                          А если сравнить со всем известной: commons.apache.org/proper/commons-configuration/
                                          и менее известной: github.com/typesafehub/config?

                                          Последнюю юзает playframework, почитайте взможности: www.playframework.com/documentation/2.0/Configuration
                                          На мой взгляд- самая передовая.

                                            0
                                            У nProperty возможностей столько же. Спасибо за спецификацию от плея, включим в nProperty того, чего нет (например, инициализацию поля File из пути в конфиге)
                                            0
                                            Вышла версия 1.2:
                                            code.google.com/p/jfork/source/detail?r=13
                                            * Added ability to use prefixes in class Cfg;
                                            * Added ability to use java.io.Reader as data stream.
                                            * Added tests & samples for new features.
                                              0
                                              Отличная вещь, судя по описанию.
                                              Опробую в каком-нибудь мелко-среднем проекте.
                                                +1
                                                Подкину идею: задание одних свойств через другие.
                                                Часто требуется для путей.
                                                Например

                                                base.path=/my/path
                                                other.path=${base.path}/relative/path
                                                  0
                                                  Спасибо за идею, будет учтено :)

                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.