Как получить данные из файла property

от admin

Простой пример работы с Property файлами в Java

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

Шаг 0. Создание проекта

Начнем с того что создадим простой Maven проект, указав название и имя пакета:

Структура, которая получится в конце проекта довольно таки простая.

Как видите у нас только два файла, первый – Main.java, а второй – config.properties.

Шаг 2. Добавляем конфигурационные данные в проперти файл

Проперти файлы либо файлы свойств – предназначены, для того чтобы хранить в них какие-то статические данные необходимые проект, например логин и пароль к БД.

Давайте добавим в наш config.properties логин и пароль (это любые данные, для того чтобы продемонстрировать работу с property файлами).

Содержимое config.properties:

<ключ> – это уникальное имя, по которому можно получить доступ к значению, хранимому под этим ключом.

<значение> – это текст, либо число, которое вам необходимо для выполнения определённой логики в вашей программе.

Шаг 3. Получаем Property данные

Как можно видеть в структуре проекта выше, там есть класс Main.java давайте его создадим и напишем в нем следующее:

Обращаясь к property.getProperty(<ключ>) – вы получаете его значение.

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

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

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

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

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

Содержание

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

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

Пример №1. Загрузка конфигурации с помощью стандартного класса Properties.

Пример №2. Загрузка конфигурации с помощью nProperty.

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

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

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

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

  • Integer/int
  • Short/short
  • Double/double
  • Long/long
  • Boolean/boolean
  • String
  • Character/char
  • Byte/byte
  • AtomicInteger, AtomicLong, AtomicBoolean
  • BigInteger, BigDecimal

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Читать:
Safezone stable что это за программа

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

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

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

Самый удобный и корректный способ — №3. Библиотека nProperty позволяет работать не только с полями, но и с методами:

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

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

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

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

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

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

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

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

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

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

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

Таким образом, данные могут быть получены не только из файловой системы, но и от любого источника данных, работающего по стандартам Java. Умение работать с java.io.InputStream дает возможность библиотеке быть успешно примененной в операционных системах Android:

Замечания

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

Properties

Properties - 1

В Java принято делать программы гибкими и легко настраиваемыми. Иногда еще говорят легко конфигурируемыми (от конфигурация).

Например, твоя программа раз в час копирует файлы из некоторой директории, архивирует их и отсылает тебе на email. Для этого программе надо знать директорию, откуда брать файлы и email, куда их посылать. Такие данные принято хранить не в коде программы, а в отдельных файлах свойств – properties-файлах.

Данные в таком файле хранятся в виде пар ключ-значение, разделенные знаком равно.

Слева от знака равно – имя (ключ), справа – значение.

— Т.е. это что-то типа текстового представления HashMap?

Для удобной работы с такими файлами в Java есть специальный класс – Properties. Класс Properties унаследован от Hashtable<Object,Object>. Его даже можно рассматривать как HashTable, который умеет загружать себя из файла.

Метод Описание
void load(Reader reader) Загружает свойства из файла, представленного объектом Reader
void load(InputStream inStream) Загружает свойства из файла, представленного объектом InputStream
void loadFromXML(InputStream in) Загружает свойства из XML-файла
Object get(Object key) Возвращает значение по ключу. Метод унаследован от HashTable
String getProperty(String key) Возвращает значение свойства (строку) по ключу
String getProperty(String key, String defaultValue) Возвращает значение свойства по ключу или defaultValue, если такого ключа нет
Set<String> stringPropertyNames() Возвращает список всех ключей

Т.е. фактически тебе нужно выполнить всего две операции – загрузить в объект Properties данные из какого-нибудь файла, а затем получить эти свойства с помощью метода getProperty(). Ну и не забывай, что можешь пользоваться объектом Properties как HashMap.

Вот тебе пример:

— Ага. Т.е. мы создаем объект Properties, затем передаем в него файл. В метод load, а затем просто вызываем getProperty. Так?

— А ты еще говорил, что им можно пользоваться как HashMap? Что ты имел в виду?

— Класс Properties унаследован от Hashtable, а это – тот же HashMap, просто все методы его синхронизированы. Вот так можно просто вывести на экран все значения из файла свойств:

— Ага. Вроде все стало на свои места. Спасибо, Риша, буду пользоваться такой крутой штукой.

Пример работы с Properties в Java

В наше время почти в каждом проекте используются различные статические файлы и данные, необходимые классам для безопасного и быстрого доступа. Примером таких конфигурационных файлов являются так называемые Файлы свойств, более известные как *.properties.

.properties — файловое расширение для файлов, которые используются в основном в технологиях Java для хранения конфигурационных параметров программы. Они также могут использоваться для хранения переменных строчной типа для локализации; они известны как Property Resource Bundles.

Каждый параметр сохраняется парой двух переменных строчной типа, одна сохраняет имя параметра (так называемый ключ), а другой сохраняет значение. Каждая строка файла .properties, как правило, хранит значения одного параметра. Несколько форматов являются допустимыми для .properties, в том числе ключ = значение, ключ: значение и сам ключ.

.properties файлы могут использовать знак решетки (#) или восклицательный знак (!) как первый, не пустой символ в строке для обозначения последующего текста в качестве комментария.

Например работы с .properties в Java

Для начала подготовим среду для работы и создадим проект (Рисунок 1):

1

Рисунок 1 — Пример проекта на Maven — очень удобно и функционально

Теперь посмотрим на структуру созданного проекта (Рисунок 2):

2

Рисунок 2 — Структура нашего проекта для работы с properties

Как видим, в папке Resources уже есть файл под названием config.properties. Вам нужно будет его создать вручную и наполнить данными, а именно (Рисунки 3-4):

3

Рисунок 3 — Создание файла путем нажатия ПКМ на папке resources -> New -> File

4

Рисунок 4 — Вводим название файла с расширением properties в появившемся диалоге и нажимаем ОК

Далее прописываем необходимые ключи и значения, как на рисунке 5:

5

Рисунок 5 — Файл с необходимыми параметрами

Пример обращения к файлу properties

Ниже представлена программа, демонстрирующая обращение к нашему конфигурационному файлу:

Похожие статьи