Spring BeanPostProcessor与动态加载数据源配置
前言:
本文旨在介绍Spring动态配置数据源的方式,即对一个DataSource的配置诸如jdbcUrl,user,password,driverClass都通过运行时指定,而非由xml静态配置定死。
Spring构造Context的参数一般只包含配置文件路径和类加载器,如果需要达到动态传入配置参数的目的,需要Spring在初始化数据源相关bean的时候能够对原有配置执行修改或替换,为方便处理,本文将定义一个名为DynamicDataSourceConfigHolder的公共类提供配置数据存储。
本文替换数据源为c3p0配置。
BeanPostProcessor简介:
Spring BeanPostProcesssor通常被称为Spring Bean回调处理器,它一般用于在实例化一个bean的前后增加一些附加操作,它会对全局的Spring bean配置生效。
Spring Bean的生命周期处理:
Spring Bean生命周期通常对应两种处理方式,一种是init-method &destroy-method, 另一种是InitializingBean的afterPropertiesSet()方法和DisposeBean的destroy()方法,BeanPostProcessor的出现使得批处理Spring bean定义有了可能。
BeanPostProcessor定义:
/**
* Factory hook that allows for custom modification of new bean instances,
* e.g. checking for marker interfaces or wrapping them with proxies.
*
* <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
* bean definitions and apply them to any beans subsequently created.
* Plain bean factories allow for programmatic registration of post-processors,
* applying to all beans created through this factory.
*
* <p>Typically, post-processors that populate beans via marker interfaces
* or the like will implement {@link #postProcessBeforeInitialization},
* while post-processors that wrap beans with proxies will normally
* implement {@link #postProcessAfterInitialization}.
*
* @author Juergen Hoeller
* @since 10.10.2003
* @see InstantiationAwareBeanPostProcessor
* @see DestructionAwareBeanPostProcessor
* @see ConfigurableBeanFactory#addBeanPostProcessor
* @see BeanFactoryPostProcessor
*/
public interface BeanPostProcessor { /**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one; if
* <code>null</code>, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; /**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding <code>bean instanceof FactoryBean</code> checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one; if
* <code>null</code>, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
以上为Spring源代码,我们重点关注它和Spring bean初始化的关系,即postProcessBeforeInitialization将会在Spring 执行bean初始化钩子(init-method或者afterPropertiesSet)之前被调用。
DynamicDataSourceConfigHolder:
package org.wit.ff; import java.util.Map; /**
* 动态数据源配置存储.
* @author ff
*
*/
public class DynamicDataSourceConfigHolder { /**
* 定义本地变量,加入存在多个Spring Context连接多个不同的数据源时,可以共用此类。
*/
private static final ThreadLocal<Map<String,String>> dynamicDataSourceConfigHolder = new ThreadLocal<Map<String,String>>(); public static void setDynamicConfig(Map<String,String> dynamicDataSourceConfig) {
dynamicDataSourceConfigHolder.set(dynamicDataSourceConfig);
} public static Map<String,String> getDynamicDataSourceConfig() {
return (dynamicDataSourceConfigHolder.get());
} public static void clear() {
dynamicDataSourceConfigHolder.remove();
} }
数据源配置文件:
db.driverClass=****
db.jdbcUrl=****
db.user=****
db.password=****
自定义bean回调处理器:
package org.wit.ff; import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils; import com.mchange.v2.c3p0.ComboPooledDataSource; /**
* Bean回调处理器.
* @author ff
*
*/
public class ComboPooledDataSourceBeanPostProcessor implements BeanPostProcessor { private String dataSourceName; @Override
public Object postProcessAfterInitialization(Object bean, String paramString) throws BeansException {
return bean;
} @Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
// 限制数据源名称和类型.
if (bean instanceof ComboPooledDataSource && dataSourceName.equals(beanName)) {
final Map<String,String> methodMatchField = new HashMap<String,String>();
methodMatchField.put("setDriverClass", "db.driverClass");
methodMatchField.put("setJdbcUrl", "db.jdbcUrl");
methodMatchField.put("setUser", "db.user");
methodMatchField.put("setPassword", "db.password");
// 从公共存储区中加载.
final Map<String, String> config = DynamicDataSourceConfigHolder.getDynamicDataSourceConfig();
ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method paramMethod) throws IllegalArgumentException, IllegalAccessException {
if(methodMatchField.containsKey(paramMethod.getName())){
ReflectionUtils.invokeMethod(paramMethod, bean, config.get(methodMatchField.get(paramMethod.getName())));
}
}
});
}
return bean;
} public void setDataSourceName(String dataSourceName) {
this.dataSourceName = dataSourceName;
} }
Spring 配置文件dynamicDatasource/applicationContext.xml:
<!-- 加载properties配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- 这里支持多种寻址方式:classpath和file -->
<value>classpath:dynamicDatasource/dbconfig.properties</value>
</list>
</property>
</bean>
<!-- 回调处理器.-->
<bean id="dynamicDataSourceBp" class="org.wit.ff.ComboPooledDataSourceBeanPostProcessor" >
<property name="dataSourceName" value="dataSource" />
</bean> <!-- 数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${db.driverClass}" />
<property name="jdbcUrl" value="${db.jdbcUrl}" />
<property name="user" value="${db.user}" />
<property name="password" value="${db.password}" />
</bean>
测试示例:
Map<String,String> dynamicDataSourceConfig = new HashMap<String,String>();
dynamicDataSourceConfig.put("db.driverClass", "com.mysql.jdbc.Driver");
dynamicDataSourceConfig.put("db.jdbcUrl", "jdbc:mysql://127.0.0.1:3306/menlo3?autoReconnect=true&characterEncoding=utf-8");
dynamicDataSourceConfig.put("db.user", "root");
dynamicDataSourceConfig.put("db.password", "root");
DynamicDataSourceConfigHolder.setDynamicConfig(dynamicDataSourceConfig);
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"classpath:dynamicDatasource/applicationContext.xml"}); //执行一段操作数据库的逻辑验证即可. assertNotNull(applicationContext);
Spring BeanPostProcessor与动态加载数据源配置的更多相关文章
- atitit.动态加载数据库配置in orm hibernate mybatis
atitit.动态加载数据库配置in orm 1. 动态加载数据库配置的优点::: 1 1.1. 组合多个配置文件... 1 1.2. 连接多个数据库 1 2. 基本的流程:::getCfg内存对象, ...
- ReportViewer动态加载数据源
ReportViewer主要用于打印和导出数据到pdf或excel,接下来将简单做一张Northwind的Products表的统计报表. (最终图) 一.新建一张报表 二.添加数据集 添加xsd文件后 ...
- 在VC中动态加载ODBC的方法
在使用VC.VB.Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置ODBC数据源.对于一般用户而言,配置ODBC数据源可能是一件比较困难的工作.而且,在实际应用中,用户往往 ...
- spring动态加载(刷新)配置文件 [复制链接]
待验证 在程序开发时,通常会经常修改spring的配置文件,不得不重启tomcat来加载spring配,费时费力.如果能在不重启tomcat的情况下,手动动态加载spring 配置文件,动态重启读取s ...
- Spring Boot 如何热加载jar实现动态插件?
一.背景 动态插件化编程是一件很酷的事情,能实现业务功能的 解耦 便于维护,另外也可以提升 可扩展性 随时可以在不停服务器的情况下扩展功能,也具有非常好的 开放性 除了自己的研发人员可以开发功能之外, ...
- 分享个刚写好的 android 的 ListView 动态加载类,功能全而代码少。
(转载声明出处:http://www.cnblogs.com/linguanh/) 简介: 该ListView 实现动态加载数据,为了方便用户充分地自定义自己的数据源.点击事件,等核心操作, ...
- 关于设置SQLPLUS提示符样式的方法----登陆配置文件,动态加载提示符
工作中用到 sqlplus mdsoss/mdsoss, 所以来了解一下sqlplus (C shell .cshrc文件里中alisa) 关于设置SQLPLUS提示符样式的方法 12638阅读 1评 ...
- shiro不重启动态加载权限
最近一朋友让我帮他做一个后台权限管理的项目.我就在我原来的项目加加改改但是还是不理想,查了不少资料也走了不了弯路...... shiro基本的配置我就不多说了这个很简单自己查查资料就完成----下面是 ...
- OSGI动态加载删除Service bundle
OSGi模块化框架是很早就出来的一个插件化框架,最早Eclipse用它而出名,但这些年也没有大热虽然OSGi已经发布了版本1到版本5.现在用的最多的,也是本文讲述基于的是Equinox的OSGi实现, ...
随机推荐
- JQuery -- Dom操作, 示例代码
1.内部插入节点 * append(content) :向每个匹配的元素的内部的结尾处追加内容 * appendTo(content) :将每个匹配的元素追加到指定的元素中的内部结尾处 * ...
- ROC 曲线,以及AUC计算方式
ROC曲线: roc曲线:接收者操作特征(receiveroperating characteristic),roc曲线上每个点反映着对同一信号刺激的感受性. ROC曲线的横轴: 负正类率(false ...
- android传感器使用
android传感器的使用以加速度传感器.方向传感器.磁场.压力.温度.光感.特别的距离传感器为例介绍. 首先所在的类需要implement SensorEventListener.使用传感器分为以下 ...
- C++(十五) — sizeof 运算符
1.基本数据类型 sizeof 是一个关键字,它是一个编译时运算符,用于判断变量或数据类型的字节大小. sizeof 运算符可用于获取类.结构.共用体和其他用户自定义数据类型的大小. 使用 sizeo ...
- poj32072-sat模板题
tarjan扫一遍后直接判断 最关键的地方就是建边(x[i] <= x[j] && y[i] >= x[j] && y[i] <= y[j]) || ...
- 【zzulioj-1676】与同学比身高(综合)
题目链接: http://acm.zzuli.edu.cn/problem.php?id=1676 题目描述 新学年开学了,学校又迎来了一批新同学,已知部分同学之间的身高关系,请列出可推断出的同学之间 ...
- CSS:Tutorial one
1.Three Ways to Insert CSS External style sheet Internal style sheet Inline style External Style She ...
- DRF 中 解决跨域 与 预检
DRF 中 解决跨域 与 预检 1 跨域 浏览器的同源策略: 对ajax请求进行阻拦 ps: 对href src属性 不限制 只有浏览器会阻止,requests模块不会存在跨域 (1)解决方案1 JS ...
- Electron 使用 Webpack2 预编译 Electron 和 Browser targets
Electron 使用 Webpack2 预编译 Electron 和 Browser targets 前一篇文章说了说怎样使用 Webpack2 预编译 Electron 应用,但是有时候我们希望使 ...
- js实现城市二级联动列表
这个是一个同事写的,我看着有用,就cv下来了. 程序功能主要逻辑是: 1.当一级标签市显示默认状态 '-请选择-'时,二级标签要隐藏 2.一级标签选中城市时,二级标签显示在页面,并列出响应市区 3.当 ...