首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

classloader实战:一个程序使用相同数据库的两个不同版本的jar包

问题场景

现在很多工程为了功能扩展,都给出了插件化的方式。只需要用户配置好配置文件,提供好需要的jar包,就能完成响应功能。本文说一下,数据转存数的功能实现。现在项目一般都离不开数据库,自己本身的项目就会带这驱动包,但是也会有这样的一种需求,就是数据额外存储的定制化,当产生的数据在自己项目的流程中不满足现在使用。例如做报表,项目本身产生数据,但是需要把里面的一部分数据拿出来和其他文本数据结合,产生新的数据。或者现有的数据进行时间的整合,直接变成周数据,月数据,年数据。这些都是需要根据不同用户自己设定的。这些数据往往是需要另外的数据库的,这就带来了一个问题,项目本身有一个jar包,例如Mysql4.5的,客户想组织信息存入mysql5.5,jar包的类是相同的,这样就带来了驱动加载的问题,因为类的相同的,而且驱动不是完全不兼容,而是在使用上会出问题。典型的就是ojdbc14,用这个版本的驱动建立数据库使用语句池缓存会有问题,ojdbc5,6就没事。(毕竟语句池缓存能带来性能上优化,不能说为了兼容驱动放弃性能,而且其他潜在的问题还没有暴露)。

问题分析

本质上就是一个获取正确连接的问题,我们正常使用驱动第一件事就是class.forname("xxxxx"),mysql(注册肯定是需要注册的,但是不一定发生在class.forname上)的话就是把驱动类注册到了DriverManager上,然后我们通过DriverManager来获取connection对象。getConnection的过程可以分为以下步骤。

1,验证对象

这里的classloader的调用getConnection的对象的classloader,driver则是驱动注册到DriverManager的对象,这里通过classloader再次加载driver类,这里如果加载到的class和driver是一样的,当前的classloader要不是加载驱动的classloader,要不就是其子classloader,以此保持不会出现类A非类A的问题(classloader的经典问题)。

2,获取连接

这里就是获取连接的过程,registeredDrivers就是已经注册的驱动,它是一个集合,下面是声明。

这样可以看到,只要通过检验就获取连接,获取成功就返回,这就是我们获取的connection对象。这里的一个明显问题就是集合是一个线程安全的链表,谁先被加载谁先去获取connection对象。如果按照以往的方式,这里有两个难题,一个是classloader的验证,一个是遍历的顺序。看起来只要让classloader验证正确,那么就可以保证遍历的的时候正确的loader加载正确的驱动了。很明显jdk不让你这么玩,因为这个方法是private的,这就必须保证你调用的classloader和注册的classloader必须是同一个,注册的还简单,但是调用的classloader也这样,那就必须把代码实现打包出来,这样很不利于我们管理。

以上我们看通了java的连接流程,那么我们尝试做精简,提取关键代码。

其实获取连接就这样一句话,剩下的内容都是为了保证这句话正确执行的校验,现在我们自己来保证这个校验,这样就把关键逻辑简化成获取Driver对象,获取Connection对象,2个过程。无论怎么思考,都绕不过classloader,所以自定义classloader是肯定绕不过去了。我们借助classloader来保证加载的驱动类是正确的,这点就比较容易的实现了。

实现

首先需要一个违背双亲委托的classloader,不知道怎么写的可以查看我上一篇文章。地址:https://my.oschina.net/xpbob/blog/761436

这种classloader很好的保证了我们加载的安全性,因为地址是我们指定的,用户扔进去多个相同版本那和在was的lib目录下扔入效果一样,都不保证正常运行。简单写一个使用方式。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180603G0NO6200?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券