SSM框架分析-MyBatis-0-jdbc代码分析
本文尝试分析下Java中JDBC如何跟mysql交互的,即jdbc中涉及了哪些底层交互。
简单的查询代表如图所示,代码逻辑:①反射方式加载我们导入的类包②DriverManager获取连接③通过连接获取statement④传入sql并执行⑤通过ResultSet获取结果。
疑问:import的是为什么java下的类,为什么木有使用mysql包中的类呢?桥接模式在哪里体现呢?PrepareStatement中如何实现预编译呢?
------------------------------------------------开始代码分析-------------------------------------------------------------
首先查看通过反射加载mysql下的Driver发生了什么
它继承了NonRegisteringDriver这个类,我们在进入这个类,发现它也同在package com.mysql.jdbc;下,包含静态代码块如下图。根据static代码的执行顺序,先是父类static代码块执行,然后是子类(也就是com.mysql.jdbc.Driver类)执行static代码块。
进入AbandonedConnectionCleanupThread类,这个类继承了Thread类,通过上边代码可以看出被设置成守护进程,观察其run方法,发现这里出现了Reference对象,它是软弱虚引用的基类,跟垃圾回收有关,<>泛型说明了这是一个有关“连接对象”的回收引用。
让我们在返回NonRegisteringDriver类中,看看refQueue.remove(100L)语句中发生了什么。ReferenceQueue是当一个connection对象被GC后,被放入到队列中,其中remove(long timeout)代表移除此队列中的下一个引用对象,阻塞到有一个对象变得可用或者给定的超时期满了为止。上图中,所以就是每隔100个单位时间看一看有木有被回收的connection,如果有的话,进入if语句中,接下来的代码还需要再返回NonRegisteringDriver,不过可以了解,它是做了一定量io资源关闭及回收。
小小结:后台开启了一个做资源回收的守护线程。负责一定的connection引用的回收。
--------------------------------------------------------------------------------------------------------------------------------
接下来终于可以看一下com.mysql.jdbc.Driver类中的static代码块了。默认的Driver类构造器中貌似什么逻辑都没执行,那么接下来就要研究DriverManager中发生了什么。
接下来要研究DriverInfo这个类和registeredDrivers,不过可以大致看出,第一次执行应该意味着这个driver还没被注册的话,先注册一下(防止重复注册,因为Class.forName()函数一不小心写了好几次的情况哈)。DriverInfo是DriverManager底下的内部类,不过好像这里也没有发生什么。最后再看registeredDrivers.addIfAbsent()了。
哎,这里用了个CopyOnWriteArrayList,是个写时复制的list(没记错的话应该是如果要添加新的object,他会创建一个新的list,然后最后指向它,成为新的list),是个静态对象,其中的对象是DriverInfo,addIfAbsent函数表示如果DriverInfo不存在,那么添加新的。(其实这里想了想,最终添加的driverinfo,可以理解为添加了com.mysql.jdbc.Driver,更可以理解为添加了NonRegisteringDriver,这个类里边有很多的实现细节)
至此Class.forName()应该是执行完毕了,主要干了两件事情:开启一个资源回收的守护线程和在java.sql. DriverManager类中的list中添加了mysql的Driver信息。(本人思考到这里再结合桥接模式,可不可以认为DriverManager就是个桥的管理员,实现细节交给了不同的Driver)
-----------------------------------------Connection c = DriverManager.getConnection()-----------------------
接下来要看getConnection发生了什么了。思考,哪里能找到上一步中的DriverInfo呢?
在这里是把字符串传入了进来,账号密码,要做连接数据库的处理了,最后在执行另一个重载的getConnection函数返回connection。先等一下,@CallerSensitive这个注解是做什么的呢?后边Reflection.getCallerClass(),貌似是返回调用者的类。
http://blog.****.net/HEL_WOR/article/details/50199797 这个上边有关于注解的解释没太看懂
到下图这里的getConnection,前半部分依然围绕着上边的的caller,然后涉及类加载器,获取当前线程的类加载器或者是 caller即调用者的类的类加载器。(存疑)
之后的下一个逻辑是确保Drivers已经初始化。进入此函数发现涉及syn同步以及系统环境变量等函数,不做深究。
接下来就要找到DriverInfo了,registeredDrivers就是之前我们说过的CopyOnWriteList,这里通过注释看到说要判断调用者是否有权限去加载driver(通过前边的classLoader和caller来做判断,这里我猜测是必须要Driver的类加载器和caller调用者的类加载器是相同的才能执行,因为可能有多个Driver,而我们要找出符合我们需求的那个Driver也就是之前JDBC中Class.forName()导入的Driver,存疑),因为正常执行代码是肯定能返回connection的,所以这里逻辑if条件应该判断为true了。
那么要获得connection是通过下边的代码
也就是通过com.mysql.jdbc.Driver类调用connect函数,传入url和账号密码,具体实现细节都在NonRegisteringDriver类中。
函数首先对url字符串进行解析,可以看到如果前边是loadbalance或者replication的字符串,返回的连接是不同的(额,但我好像从来没用过)。
接下来走到底下的parseURL函数,可以看到,它是对我们传入的url进行解析,从//开始,分隔?之前之后的部分,然后在新的urlProps中传入属性,诸如socketFactory(跟数据库建立tcp连接)、configNames设置的参数名等等。然后关于上图中底下的else if判断条件NUM_HOSTS的,在parseURL后边有关于其的设置。(貌似是如果没有写url和端口号,那么就就把url和port默认设置成本机localhost的ip和3306的端口号,然后NUM_HOSTS设置为1,好像是哈)
最后终于要到connect函数中生成connection的语句了
.
最后再来看看这个ConnectionImpl里边有啥。首先是一堆属性的设置,看了好多感觉也都是属性相关的设置,然后在这个函数的最后,又把自己传给了NonRegisterDriver。看来追踪connection对象的垃圾回收情况的。
不知道是不是没有细看,这一模块的代码只看到socketFactory的相关属性的设置但并没有建立起来socket连接?存疑,继续往下看
下面该Statement s = c.createStatement()这行了,从connection对象中获取statement。具体的实现细节在ConnectionImpl类中。返回的是StatementImpl这个实现类。
最后是ResultSet resultSet = s.executeQuery(sql);执行查询语句。上来有个syn块,锁的是Connection对象,那么就说明要保证连接对象的多线程安全。额,这个函数剩下的对我来说的确有点太看不懂了。(存疑,留着吧)