在非互联网的项目中,有时候需要跨库使用数据源。为了更加方便的使用,基本上都会开发一款动态数据源。以下是基于Spring的动态数据源源码:
public class DataSourceContext extends AbstractRoutingDataSource { private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>(); public static void setDataSourceKey(String dataSource) { dataSourceKey.set(dataSource); } @Override protected Object determineCurrentLookupKey() { return dataSourceKey.get(); } } |
继承AbstractRoutingDataSource, 再使用数据源之前进行指定,可以手动指定如:
DataSourceContext.setDataSourceKey(dataSourceKey); |
也可以通过AOP的方式实现,如:
public class SpecifyDataSourceAop { public void before(JoinPoint point) { MethodSignature methodSignature = (MethodSignature) point.getSignature(); Method method = methodSignature.getMethod(); SpecifyDataSource dataSource = method.getAnnotation(SpecifyDataSource.class); storeKey(dataSource); assignDataSourceKey(); } public void after(JoinPoint point){ removeKey(); assignDataSourceKey(); } private void storeKey(SpecifyDataSource dataSource){ if(dataSource == null) { //if dataSource == null then assign defaultDataSource putDSKey2Store(DataSourceContants.DEFAULT_DATASOURCE); }else if (null != dataSource) { // choose SpecifyDataSource`s name putDSKey2Store(dataSource.name()); } } private void putDSKey2Store(String key){ DATASOURCE_KEYS.get().addLast(key); } private String assignDataSourceKey(){ LinkedList<String> keys = DATASOURCE_KEYS.get(); DataSourceContext.setDataSourceKey(dataSource); return dataSource; } private String removeKey(){ return DATASOURCE_KEYS.get().removeLast(); } private static final ThreadLocal<LinkedList<String>> DATASOURCE_KEYS = new ThreadLocal<LinkedList<String>>(){ @Override protected LinkedList<String> initialValue() { return new LinkedList<String>(); } }; } |
默认为dataSource 数据源,使用时如果在接口层加上注解 @SpecifyDataSource 并指定数据源 (name="xxDataSource"), 在调用此接口实现的方法时,会切换到xxDataSource,调用完成后切回上一个数据源。
注解类为
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface SpecifyDataSource { /** * 指定数据源 * @return */ String name() default "dataSource"; } |
针对此动态数据源的配置文件如下:
<!-- 配置多数据源映射关系 --> <bean class="org.nforgo.component.datasource.DataSourceContext"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="dataSource" value-ref="dataSource"></entry> <entry key="ucDataSource" value-ref="xxDataSource"></entry> </map> </property> <!-- 默认目标数据源为你主库数据源 --> <property name="defaultTargetDataSource" ref="dataSource"/> </bean> <bean id="specifyDataSourceAop" class="org.nforgo.component.datasource.SpecifyDataSourceAop"></bean> <!-- AOP拦截配置 --> <aop:config> <aop:aspect id="dataSourceAspect" ref="specifyDataSourceAop" order="1"> <aop:pointcut id="dsPoint" expression="execution(* org..*.business.manage..*.*(..))"/> <aop:before method="before" pointcut-ref="dsPoint"/> <aop:after-returning method="after" pointcut-ref="dsPoint"/> <!--<aop:after-throwing method="after" pointcut-ref="dsPoint"/>--> </aop:aspect> </aop:config> |
此动态数据源还有很多需要更新的地方,在真正使用时再做二次开发,如事务的处理,在接口层之外的数据源个性化指定等等。