javascript
spring aop根据jdbctemplate方法名动态设置数据源 -尊龙游戏旗舰厅官网
2019独角兽企业重金招聘python工程师标准>>>
说明:现在的场景是,采用数据库(mysql)复制(binlog)的方式在两台不同服务器部署并配置主从(master-slave)关系; 并需要程序上的数据操作方法来访问不同的数据库,比如,update方法访问主数据库服务器,query方法访问从数据库服务器。 即把“增删改”和“查”分开访问两台服务器,当然两台服务器的数据库同步事先已经配置好。 然而程序是早已完成的使用spring jdbctemplate的架构,如何在不修改任何源代码的情况下达到<本文标题>的功能呢? 分析: 1.目前有两个数据源需要配置到spring框架中,如何统一管理这两个数据源? jdbctemplate有很多数据库操作方法,关键的可以分为以下几类(使用简明通配符):execute(args..)、update(args..)、batchupdate(args..)、query*(args..) #args.. 表示可为任意个参数或无参数。 2.如何根据这些方法名来使用不同的数据源呢? 3.多数据源的事务管理(非本文关注点)。 实现: spring配置文件applicationcontext.xml(包含相关bean类的代码) 1.数据源配置(省略了更为详细的连接参数设置): 01
dynamicdatasource类继承了spring的抽象类abstractroutingdatasource,而abstractroutingdatasource本身实现了javax.sql.datasource接口(由其父类抽象类abstractdatasource实现),因此其实际上也是一个标准数据源的实现类。该类是spring专为多数据源管理而增加的一个接口层,参见spring-api-doc可知: abstract datasource implementation that routes getconnection() calls to one of various target datasources based on a lookup key. the latter is usually (but not necessarily) determined through some thread-bound transaction context. 它根据一个数据源唯一标识key来寻找已经配置好的数据源队列,它通常是与当前线程绑定在一起的。 查看其源码,知道它还实现了spring的初始化方法类initializingbean,这个类只有一个方法:afterpropertiesset(),由spring在初始化bean完成之后调用(根据该方法名联想应该是设置完所有属性后再调用,实际也是如此): 01 public void afterpropertiesset() { 02 if (this.targetdatasources == null) { 03 throw new illegalargumentexception("targetdatasources is required"); 04 } 05 this.resolveddatasources = new hashmap(this.targetdatasources.size()); 06 for (iterator it = this.targetdatasources.entryset().iterator(); it.hasnext(); ) { 07 map.entry entry = (map.entry)it.next(); 08 object lookupkey = resolvespecifiedlookupkey(entry.getkey()); 09 datasource datasource = resolvespecifieddatasource(entry.getvalue()); 10 this.resolveddatasources.put(lookupkey, datasource); 11 } 12 if (this.defaulttargetdatasource != null) 13 this.resolveddefaultdatasource = resolvespecifieddatasource(this.defaulttargetdatasource); 14 } 查看其具体实现可知,spring将所有已经配置好的数据源存放到一个名为targetdatasources的hashmap对象中(targetdatasources属性必须设置,否则异常;defaulttargetdatasource属性可以不必设置)。只是把数据源统一存到一个map中并不能做什么,关键是它还重写了javax.sql.datasource的getconnection()方法,该方法无论你在何时使用数据库操作相关的方法时都会使用到,即使ibatis、hibernate、jpa等进行多层封装的框架底层还是使用最普通的jdbc来实现。 01 public connection getconnection() throws sqlexception { 02 return determinetargetdatasource().getconnection(); 03 } 04 protected datasource determinetargetdatasource() { 05 object lookupkey = determinecurrentlookupkey(); 06 datasource datasource = (datasource)this.resolveddatasources.get(lookupkey); 07 if (datasource == null) 08 datasource = this.resolveddefaultdatasource; 09 if (datasource == null) 10 throw new illegalstateexception("cannot determine target datasource for lookup key [" lookupkey "]"); 11 return datasource; 12 } 13 protected object resolvespecifiedlookupkey(object lookupkey) { 14 return lookupkey; 15 } 16 protected abstract object determinecurrentlookupkey(); 省略部分校验代码,这里有一个必须的关键方法:determinecurrentlookupkey,也是一个抽象的有你自己实现的方法,从这个方法返回实际要使用的数据源的key(也即在前面配置的数据源bean的id)。从spring-api-doc中可以看到详细说明: determine the current lookup key. this will typically be implemented to check a thread-bound transaction context. allows for arbitrary keys. the returned key needs to match the stored lookup key type. 它允许任意类型的key,但必须是跟保存到数据源hashmap中的key类型一致。我们可以在spring配置文件中指定该类型,网上看到有仁兄使用枚举类型的,是一个有不错约束性的主意。 我们的“test.my.serivce.ds.dynamicdatasource”实现了这个方法,可见具体的数据源key是从customercontextholder类中获得的,并且也是使用key与当前线程绑定的方式: 01 public class customercontextholder { 02 private static final threadlocal contextholder = new threadlocal(); 03 public static void setcustomertype(string customertype) { 04 contextholder.set(customertype); 05 } 06 public static string getcustomertype() { 07 return (string) contextholder.get(); 08 } 09 public static void clearcustomertype() { 10 contextholder.remove(); 11 } 12 } 我们也可以使用全局变量的方式来存储这个key。我之前并不知道java.lang.threadlocal,那就充点电吧:http://java.dzone.com/articles/java-thread-local-–-how-use 甚至有一位评论者一针见血的指出问题来: why is userthreadlocal declared public? afaik, threadlocal instances are typically private static fields. also, threadlocal is a generic type, it is threadlocal
前置方法就是调用“test.my.serivce.ds.beforeadvice”类的某个set方法: 1 public class beforeadvice { 2 public void setmasterdatasource() { 3 customercontextholder.setcustomertype("master"); 4 } 5 public void setslavedatasource() { 6 customercontextholder.setcustomertype("slave"); 7 } 8 } 当前线程就会保存下设置进去的key名称并随时可以调用。 最后再配置一个jdbctemplate bean即可。 1
转载于:https://my.oschina.net/u/1388978/blog/306799
总结
以上是尊龙游戏旗舰厅官网为你收集整理的spring aop根据jdbctemplate方法名动态设置数据源的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 肾炎治疗有效方(湿热壅滞三焦,气机不利)
- 下一篇: 阿里云主机安装memcached扩展优化