欢迎访问 生活随笔!

尊龙游戏旗舰厅官网

当前位置: 尊龙游戏旗舰厅官网 > 前端技术 > javascript >内容正文

javascript

spring aop根据jdbctemplate方法名动态设置数据源 -尊龙游戏旗舰厅官网

发布时间:2025/1/21 javascript 23 豆豆
尊龙游戏旗舰厅官网 收集整理的这篇文章主要介绍了 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 04 06 07 08 09 10 11 12 15 17 18 19 20 21 22 23 25 26 30 31 32 首先定义两个数据源(连接地址及用户名等数据存放在properties属性文件中),spring可以设置多个数据源,究其根本也不过是一个普通bean罢了。 关键是id为“datasource”的这个bean的设置,它是这个类“test.my.serivce.ds.dynamicdatasource”的一个实例: 1 import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource; 2 3 public class dynamicdatasource extends abstractroutingdatasource { 4 @override 5 protected object determinecurrentlookupkey() { 6 return customercontextholder.getcustomertype(); 7 } 8 }

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. an important benefit of threadlocal worth mentioning (from 1.4 jvms forward), is as an alternative to synchronization, to improve scalability in transaction-intensive environments. classes encapsulated in threadlocal are automatically thread-safe in a pretty simple way, since it's clear that anything stored in threadlocal is not shared between threads. threadlocal是线程安全的,并且不能在多线程之间共享。根据这个原理,我写了下面的小例子以便进一步理解: 01 public class test { 02 private static threadlocal tl = new threadlocal(); 03 public static void main(string[] args) { 04 tl.set("abc"); 05 system.out.println(tl.get()); 06 new thread(new runnable() { 07 public void run() { 08 system.out.println(tl.get()); 09 } 10 }).start(); 11 } 12 } 做到这里,我们已经解决了第一个问题,但似乎还没有进入正题,如何根据jdbctemplate方法名动态设置数据源呢? 2.spring aop切入jdbctemplate方法配置: 01 02 03 04 06 08 09 10 12 13 可以看到我已经使用aop:aspect将jdbctemplate的4类方法进行拦截,并使用前置通知的方式(aop:before)在执行这些方法之前调用其他方法,具体的aop表达式语言的含义我就不细说了。 根据最开始的说明,分别对update操作和select操作进行拦截并调用不同的方法,这个方法到底是什么呢? 其实就是给threadlocal设置数据源的名字(key),以便dynamicdatasource知道到底是使用哪一个数据源。

前置方法就是调用“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 3 4 附注: 1.在解决过程中遇到的一个问题: spring异常:no matching editors or conversion strategy found 参考:http://blog.csdn.net/zzycgfans/article/details/6316081 引用:spring注入的是接口,关联的是实现类。 这里注入了实现类,所以报异常了。 2.本文主要参考的文章有: 该文还包含事务管理的配置:http://hi.baidu.com/litianyi520/blog/item/71279e3e180db6f1838b1327.html 该文与多数据源的设置对我有一定的启发(此外还包含测试用例):http://oiote.blog.sohu.com/74596942.html 之前做过ibatis采用ehcache和oscache做缓存的配置,这篇有点类似:http://lihaiyan.iteye.com/blog/127818 多数据源的一些实际场景分析,理论重于实际:http://hi.baidu.com/freeway2000/blog/item/ba0906f4946fa8eb7709d716.html 此外,javaeye(现为iteye)的一些文章也是有参考价值的:http://www.iteye.com/wiki/blog/1229655 eof.最初的设想到这里变成了现实。本文讲述了“spring aop根据jdbctemplate方法名动态设置数据源”的整个实现过程和一些浅显的分析。 使用这样配置后在实际使用中发现仍然有问题。比如,调用jdbctemplate的update方法后立即调用query方法查询该条记录,或者使用以下方法: this.jdbctemplate.update(new preparedstatementcreator(), keyholder) 因为数据库复制有同步间隙,这个时间晚于程序的调用,就会出现查询不到数据的情况,实际上是数据还未同步到从服务器。期待更好的尊龙游戏旗舰厅官网的解决方案!

转载于:https://my.oschina.net/u/1388978/blog/306799

总结

以上是尊龙游戏旗舰厅官网为你收集整理的spring aop根据jdbctemplate方法名动态设置数据源的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得尊龙游戏旗舰厅官网网站内容还不错,欢迎将尊龙游戏旗舰厅官网推荐给好友。

网站地图