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.5 rebate.amount 15.99 smtp.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.5 rebateAmount=15.99 smtpIp=173.194.79.16 ] |
Ссылка: Spring 3.1 — Загрузка свойств для конфигурации XML из базы данных от нашего партнера JCG Аллена Джулии в блоге YK’s Workshop .