在"@Value值注入及配置文件组件扫描方式"这篇blog中,说过从properties文件读取配置项,然后通过@Value进行注入时需要注意的问题。实际上,有时候不光从properties文件中读取配置项,还要从数据库中读取配置项。从数据库中读取配置项可以通过PropertyPlaceholderConfigurer的子类来实现。
假设数据源配置在spring-datasource-local.xml中,先在该文件中配置properties文件的路径(这里是为了从文件中读取配置项)。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="locations">
<value>classpath:config/resource/application.properties</value>
</property>
</bean>
然后配置
<bean class="cn.tonghao.properties.config.ExtPropertyPlaceholderConfigurer">
<property name="settings">
<list>
<value>setting</value>
</list>
</property>
<property name="placeholderPrefix" value="@{"/>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ds" ref="xxx"></property>
</bean>
其中ExtPropertyPlaceholderConfigurer是继承了PropertyPlaceholderConfigurer的子类,ds为数据源(配置稍后贴出),具体实现如下:
public class ExtPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{
private static final Logger logger = LoggerFactory.getLogger(PropertyPlaceholderConfigurer.class);
private List<String> settings;
private DataSource ds;
private String sql = "select property_key,property_value from property_config_data where property_type = ? order by property_type";
/**
* 重载父类
*/
@Override
protected void processProperties(ConfigurableListableBeanFactory factory, Properties props)
throws BeansException {
Properties location_props = null;
try {
//获取父类配置文件
location_props = super.mergeProperties();
} catch (IOException e) {
logger.error(" location_props Error ",e);
}
if (location_props == null) {
location_props = new Properties();
}
Connection conn = null;
try {
conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
String suffixType=getSuffixType();
for (String type : settings) {
ps.setString(1,type);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
String key = rs.getString("property_key");
if(key.startsWith(" ")||key.endsWith(" ")){
logger.warn("************************************************ key contain space,key=[{}]",key);
key=key.trim();
}
if(StringUtils.isBlank(key))
{
continue;
}
String value = rs.getString("property_value");
if(null != value)
{
if(key.startsWith("system.")){
System.setProperty(key.substring(7), value);
} else {
location_props.put(key,value);
}
}
}
}
} catch (Exception e) {
logger.error("SQL Erroe",e);
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
logger.error("Close Error",e);
}
}
super.processProperties(factory, location_props);
}
//getter和setter略
}
可以看到,bean实例化时会调用processProperties方法,从数据库中查询property_type = setting的记录,然后把对应的properties_key和properties_value放入Property类型的内存对象location_props中。然后就可以通过“@{xxxx(这里的xxx是location_props对象的key,也是数据库中properties_key字段)}”来进行注入了。
数据源ds配置如下:
<bean id="dataSource_xxx" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl"
value="xxxxxx(数据库地址)?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true" />
<property name="user" value="xxx"/>
<property name="password" value="xxx"/>
<!--连接池中保留的最大连接数。默认值: 15 -->
<property name="maxPoolSize" value="10"/>
<!-- 连接池中保留的最小连接数,默认为:3-->
<property name="minPoolSize" value="2"/>
<!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->
<property name="initialPoolSize" value="2"/>
<property name="maxIdleTime" value="56"/>
</bean>
其他文件省略。
测试demo如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:config/spring/spring-web.xml", "classpath:config/spring/spring-datasource-local.xml"})
public class GetProperFromDsTest {
@Value("@{ly.key}")
private String key;
@Value("@{ly.key}")
private String aliasKey;
@Test
public void getProperties(){
System.out.println(key);
System.out.println(aliasKey);
}
}
分别输出properties文件中配置的ly.key和数据库中配置的ly.key。