如何使用动态代理,自己写一个数据库连接池?

今天是刘小爱自学Java的第68天。

感谢你的观看,谢谢你。

话不多说,开始今天的学习:

在学线程的时候,学过线程池的概念。

当时举了个地铁临时票的例子,来说明线程池的作用。

其实数据库连接池也是一样的道理。

也就是将数据库连接放到一个池子里面,需要使用时就去取,这样的话,就不用每次都自己获取连接了。

用一张图片来说明:

①普通方式获取连接

用户每访问一次数据库就要获取一次连接,这样就会有几个问题:

  • 连接使用完后就会被销毁,造成资源浪费。

  • 如果用户有很多个,要获取很多次连接。

  • 频繁的连接数据会造成资源消耗过多,从而使数据库死机。

②连接池获取连接

连接池的好处在于:

每次使用完连接后将其放回连接池,资源重复利用,更能提高程序性能。

无论多少个用户,都只需要从连接池中获取就可以了。

一、自定义连接池

事实上关于连接池已经有框架将其封装好了,我们可以直接用。但现在是学习阶段,知其然也要知其所以然。

我们不妨自定义一个连接池,看看具体是如何操作的,并且这样也能更好地去学习连接池框架。

①实现DataSource接口

dataSource是Java中的一个接口,也是Java制定的一个规范,不同的连接池都需要实现这个接口。

MyDataSource是我们自定义的一个连接池,自然也是需要实现该接口。

②创建连接池容器

为什么要用LindkedList?

  • 连接池中的连接被取走,相当于删除了该连接。

  • 用户用完连接将其还回连接池,相当于增加了该连接。

  • 所以连接池要经常地进行增加和删除。

LinkedList集合的底层是链表,特点增删快查询慢,所以选择使用LinkedList。

③初始化连接池

也就是自定义连接池的构造方法,通过参数count可以指定连接池中连接的数量。

④从连接池中获取连接

从连接池中拿走了一个连接,也就相当于将连接池中的该连接删除了。

⑤用完将连接还回来

自定义一个backToPool()方法。

使用完连接后调用该方法将连接还回来。

这也就相当于将该连接添加进连接池中。

自定义连接池写完,做一个测试:

①创建连接池

我们这边给连接池初始化6个连接。

②获取连接

现在只需要从连接池中获取连接就好了。

③预编译以及处理结果

这个昨天刚学习过,不再赘述。

④释放资源

使用昨天封装在工具类JdbcUtil类中的release()方法,其中连接不需要被释放掉,用null代替。

⑤连接还回

连接使用完了之后将其还回到连接池中。

二、优化一之装饰模式

自定义连接池有一个问题,我们定义了一个方法backToPool(),也就是将用完连接将其还回到连接池。

对于我们开发者来说还得自己记住这个方法名,特繁琐。

有没有什么方法直接就能达到其目的?

答案是有的。我们每次使用完连接都要close(),也就是上述中被封装在工具类JdbcUtil类里的release()方法。

将连接的close方法里面的业务逻辑改成将连接还会连接池就好了。我们看看close方法的源码:

①Java中的close方法

Java中有一个接口就是Connection接口,其中就有close方法。这个接口就是Java制定的一个数据库规范。

任何数据库要连接Java就得实现Connection接口。

②MySQL中的close方法

我们使用的数据库是MySQL,MySQL它本身也确实实现了Java中的Connection接口(ConnectionImpl这个类)

也就是MySQL官网上下载的驱动,导入架包直接可以用。

至于其close方法具体是如何写的,我们不用管。

好现在我们弄明白了close方法到底是个什么回事,要改造close方法。

要知道Connection中是有很多很多方法的,并不是只有一个close方法。

在我们所学的知识中,有什么方法可以实现该目的?

  • 继承就可以了:自定义一个类MyConn,继承MySQL中的ConnectionImpl类,只重写close方法,其它方法都不管。

  • 既然有继承,那装饰设计模式也可以。

并且装饰设计模式比继承更有拓展性。

自定义一个类MyConn实现Java中的Connection接口:

①构造方法

我们要将close方法改造成将连接还回连接池,肯定要传进来一个我们正在使用的连接池。

此外还需要一个连接,根据面向接口编程原则:

参数设置成Java中的父接口Connection。

具体传参时,用mysql写的实现类连接也行,用oracle写的实现类连接也行。

②改造close方法

释放资源时会有一个这样的操作:coon.close()。

它本来的所有是将连接conn销毁掉,但我们现在不销毁而是要将其重写放回到连接池中。

所以具体的业务逻辑就是:pool.addLast(this)。

这个this就是指的coon,谁调用了close方法,这里的this指的就是谁。

③其它方法不改造

mysql中的连接是如何处理的,我们直接调用就好了。

所以其它方法都没有具体的业务逻辑,而是直接让传进来来的conn去处理。

其它方法是有很多个的,我随便截一个图:

方法一大堆,这还仅仅只是一部分,还有很多,没法一下子截图下来。

这个装饰设计模式也就类似于当初学动态代理说的明星与经纪人案例:

  • mysql中的连接,就相当于具体某个明星;

  • 我自定义的连接类,就相当于该明星的经纪人。

  • 经纪人只做沟通,具体怎么操作还是要靠明星。

  • 自定义的连接类只修改close方法,其它还是要靠mysql中的连接。

既然如何,那何不用动态代理?

三、优化二之动态代理

前面屁颠屁颠第说了这么一大堆,其实就是为了引出动态代理这个概念。

因为动态代理比较难理解,所以一步一步说明。

我们再做一个梳理:

  • 首先Java中有一个连接接口Connection。

  • 其次MySQL中有一个连接类实现了Java中的这个接口,具体怎么实现的,我们不用管。

  • 最后我们要将MySQL连接中的close方法做一个改造,同时其它的方法都不用改变。

  • 也就是说我们需要去代理MySQL中的连接。

现在使用动态代理进行改造:

①从连接池中获取连接

这个连接是什么?是MySQL中写好的实现类连接。

②动态代理改造

三个参数:

  • conn的类加载器。

  • 被代理类所要实现的接口 ,也就是Java中的Connection接口。

  • 调用处理器。

动态代理的核心其实就是这个调用处理器

后续connProxy调用close方法也好,调用其它方法也罢,其都会做一件事情:调用处理器调用invoke方法。

③返回代理类连接对象

这个已经是改造好了的,也就是说我们从连接池中获取连接,本来是MySQL中的,但是我们现在将其改造了。

所以后续用conn操作时,其实用的就是这个改造后的代理类连接对象。

④如果是close方法

后续conn调用close方法,就将其改造成我们想要的业务逻辑,也就是将连接添加到连接池中。

⑤如果是其他方法

后续conn调用其它方法(也就是connection接口中的那一堆方法),我们不用管,直接利用反射就好了。

动态代理的好处之一也在于此,其它的有哪些方法,方法名叫什么,怎么实现的,统统都不用管。

动态代理只关注我们需要的方法本身,其他不用管,比装饰设计模式更加地简洁。

以上便是用动态代理自定义的一个连接池,当然已经有开源的连接池,可以直接拿来用,不用自己写。

我写一遍的目的也就是想更好地去学习那些开源连接池。

最后

谢谢你的观看。

如果可以的话,麻烦帮忙点个赞,谢谢你。


猜你喜欢

转载自juejin.im/post/5ef14409f265da02da2bea7a