среда, 26 января 2011 г.

Использование placeholders из одного properties файла для конфигурации другого.

Иногда появляются нетривиальные задачи конфигурирования приложения для определенных нужд. Один из примеров: есть приложение, которое должно работать в разных environment'ах: development, test, production. В зависимости от environment'а используются разные файлы конфигурации/настройки. Другой пример - одно и тоже приложение может запускаться в нескольких экземплярах в одном application-сервере, при этом по разному сконфигурировано, с помощью разных файлов настроек.
Во втором случае одно из достаточно неплохих решений я опишу ниже. Файлы конфигурации - это обычные property-файлы с парами ключ значение. При этом конфигуарция всех модулей находится в одном месте, путь к которому я указываю через переменную окружения CONF_HOME. Эта переменная указывает на корень конфигурации. А в нем уже идут директории module1, module2, ..., moduleN, module1-diffconfig, modulek-n-configuration. Внутри каждой папки есть файлы типа conf.properties.


В spring чтобы использовать значения из файлов настройки в виде ${server.property1} нужно объявить в context.xml следующую конструкцию:


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


Решение следующее: в каталоге CONF_HOME для каждого экземпляра модуля заводится отдельная папка, а в ней находятся файлы настройки с одинаковыми именами,но, понятно, с разными значениями одних и тех же параметров:



Понятно, что основной вопрос - как нам изменить строчку в context.xml

чтобы вместо module1 подставлялось значение для конкретного экземляпра модуля. Решений может быть несколько, один из простых вариантов - сделать дополнительный файл настроек module.properties в котором будет одно поле nodeName=module-specific-conf. Можно сделать таким образом, чтобы это значение подменялось при мавенской сборке. Поскольку это не тема данной статьи, то варианты решения я оставлю на усмотрение любознательного читателя.


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


Но ни тут-то было. Spring при инициализации бина, не может распознать nodeName. Оказывается это связано с некой спецификой при загрузке property-файлов и работы PropertyPlaceholderConfigurer-ов. На форуме Spring'а есть несколько тем и даже запросы в джире на изменения, но все мимо. На помощь приходит SpEL - Spring Expression Language, появившийся в Spring 3. С его помощью проблема решается просто и элегантно. Вот так:


Объяснения требует лишь одно выражение:


Здесь решетка означает, что дальше идет SpEL, а дальнейшее - это доступ к бину и его свойству. Вот все просто получилось, благодаря третьему Spring.