Spring позволяет легко вводить значения, полученные из файлов свойств через PropertyPlaceholderConfigurer и (до Spring 3.1) PropertySourcesPlaceholderConfigurer (Spring 3.1). Эти классы реализуют интерфейс BeanFactoryPostProcessor, который позволяет им манипулировать значениями в файле конфигурации Spring XML до инициализации bean-компонентов. Поэтому, если вы укажете $ {jdbc.driverClassName}, который будет установлен в свойстве ‘driverClassName’, эта переменная будет заменена / заменена на значение с ключом ‘jdbc.driverClassName’ в файле свойств.
Помимо файлов свойств, таблица базы данных также может быть местом для получения пар ключ-значение. Отлично, так что просто расширьте PropertySourcesPlaceholderConfigurer, и он прочитает таблицу, содержащую пары ключ-значение, заполнит их, и все готово!
Однако есть небольшая проблема. Если bean-компонент DataSource также полагается на значения, полученные из файла свойств (например, URL-адрес JDBC, имя пользователя, пароль) и является хорошим Springers, вставьте этот bean-компонент в класс bean-компонента, расширяющий PropertySourcesPlaceholderConfigurer, контейнер bean-компонента не запустится должным образом, поскольку Переменная jdbc.driverClassName ‘не может быть разрешена. Удивительно, но факт.
Причина этого в том, что любой компонент, внедренный в класс BeanFactoryPostProcessor, будет инициировать инициализацию компонента перед тем, как будут запущены классы BeanFactoryPostProcessor. Вы знаете, инъекция зависимостей … все зависимые бины должны быть готовы, прежде чем их вводят потребителю. Так что это создает нечто вроде циклической зависимости. Все зависимости в конфигурации XML сначала разрешаются до запуска классов BeanFactoryPostProcessor.
Итак, как это сделать? Ну, есть трюк, который вы можете использовать. Класс BeanFactoryPostProcessor имеет доступ к объекту ConfigurableListableBeanFactory с помощью метода postProcessBeanFactory. Из этого объекта вы можете сделать getBean и получить ссылку на любой компонент с идентификатором. И угадайте, что, вы можете получить хваленый bean-компонент DataSource, не вызывая преждевременную инициализацию bean-компонента.
Допустим, есть таблица ‘sys_param’ со следующими данными:
|
1
2
3
4
5
|
PARAM_CD PARAM_VALUE-------------- --------------service.charge 1.5rebate.amount 15.99smtp.ip 173.194.79.16 |
DbPropertySourcesPlaceholderConfigurer показан здесь:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
package org.gizmo.labs.utils.spring;import javax.sql.DataSource;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;public class DbPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer{ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { DataSource dataSource = beanFactory.getBean(DataSource.class); DbProperties dbProps = new DbProperties(dataSource); setProperties(dbProps); super.postProcessBeanFactory(beanFactory); }} |
Класс DbProperties будет использовать ссылку на DataSource и запрашивать базу данных, чтобы получить пары ключ-значение:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package org.gizmo.labs.utils.spring;import java.util.List;import java.util.Map;import java.util.Properties;import javax.sql.DataSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.jdbc.core.JdbcTemplate;public class DbProperties extends Properties{ private final Logger logger = LoggerFactory.getLogger(DbProperties.class); private static final long serialVersionUID = 1L; public DbProperties(DataSource dataSource) { super(); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); List <map > l = jdbcTemplate.queryForList('select param_cd, param_value from sys_param'); for(Map m: l) { logger.debug('Loading from DB: [{}:{}]', m.get('PARAM_CD'), m.get('PARAM_VALUE')); setProperty((m.get('PARAM_CD')).toString(), (m.get('PARAM_VALUE')).toString()); } }} |
Чтобы продемонстрировать, что значения из таблицы правильно введены, вот класс, который действует как потребитель:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package org.gizmo.labs.utils.spring;import java.math.BigDecimal;import org.apache.commons.lang.builder.ReflectionToStringBuilder;import org.apache.commons.lang.builder.ToStringStyle;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.InitializingBean;public class DbPropConsumer implements InitializingBean{ private final Logger logger = LoggerFactory.getLogger(DbPropConsumer.class); private BigDecimal serviceCharge; private double rebateAmount; private String smtpIp; @Override public void afterPropertiesSet() throws Exception { logger.debug('I have consumed: {}', this); } public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE); } public BigDecimal getServiceCharge() { return serviceCharge; } public void setServiceCharge(BigDecimal serviceCharge) { this.serviceCharge = serviceCharge; } public double getRebateAmount() { return rebateAmount; } public void setRebateAmount(double rebateAmount) { this.rebateAmount = rebateAmount; } public String getSmtpIp() { return smtpIp; } public void setSmtpIp(String smtpIp) { this.smtpIp = smtpIp; }} |
Наконец, что не менее важно, конфигурация Spring (bean-компонент DataSource не показан, упрощенный для ясности)
|
1
|
classpath:system.properties |
Первые 2 определения бина — это классы BeanFactoryPostProcessor, и для гарантии того, что первый запускается первым, устанавливается свойство ‘order’ (ниже означает более высокий приоритет).
Для DbPropertySourcesPlaceholderConfigurer для ясности используется другой префикс и суффикс заполнителя (обратите внимание на заполнители для DbPropConsumer).
Таким образом, при запуске контейнера Spring вы сможете увидеть аналогичный вывод, как показано ниже:
|
1
2
3
4
5
6
7
8
9
|
2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbProperties, Loading from DB: [service.charge:1.5]2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbProperties, Loading from DB: [rebate.amount:15.99]2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbProperties, Loading from DB: [smtp.ip:173.194.79.16]2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbPropConsumer, I have consumed: org.gizmo.labs.utils.spring.DbPropConsumer@189b939[logger=Logger[org.gizmo.labs.utils.spring.DbPropConsumer]serviceCharge=1.5rebateAmount=15.99smtpIp=173.194.79.16] |
Ссылка: Spring 3.1 — Загрузка свойств для конфигурации XML из базы данных от нашего партнера JCG Аллена Джулии в блоге YK’s Workshop .