序言
初次打仗Java Web的时候相信都打仗过JDBC连接和JSP页面,不知是知识传授的天经地义还是初次打仗这些知识的不熟习,当时对创建JDBC连接的代码没有任何觉得和疑问,现在回忆起来,创建JDBC连接的过程还是值得一看的。
02
问题
先看下面一段代码:
这是一段大略的JDBC连接语句,但是看过这段代码之后,不知道大家会不会产生和我一样的疑问:
Class.forName(jdbcName); 这句与之后的
con = DriverManager.getConnection(dbUrl,dbUserName, dbPassword);
他们是怎么联系起来的呢?
理解Java基本语法的都知道,Java中类或者工具的调用是通过定义变量名称来引用的,比如private Student studentObj = new Student();
个中“private”指定变量的范围是私有的,第一个“Student”解释了声明的类型为Student类,“studentObj”是后面天生的Student工具的引用名称,“new Student()”是创建了一个新的student工具。
然后通过“studentObj”就可以利用“Student”工具中定义的各种属性和方法。我们大多数的利用办法是上面这种,但是创建JDBC connection显然不是这种情形。
从代码中可以看到,两句之间并没有变量通报,也没有什么静态变量定义。“TestClass”也没有继续父类,那么通过Class.forname()函数加载的驱动是如何被DriverManager.getConnection() 函数所利用的呢?
03
剖析
进到源码中:
上述代码中每个方法再往里看看是什么样子:
Reflection.getCallerClass()
forName0(className,true,ClassLoader.getClassLoader(caller), caller)
都是native方法了,没法再进入下层。
至此彷佛还没有什么头绪,那不妨看看
con = DriverManager.getConnection(dbUrl, dbUserName, dbPassword); 这句话里面做了什么
getConnection(url, info, Reflection.getCallerClass())
这里面的registeredDrivers 在哪里定义的呢?利用在这里
这是一个公有的静态方法,在对应的驱动文件源码中可以找到他的调用
04
实现流程
以是全体流程是这样的:
Class.forName函数加载了驱动类文件,在驱动类文件中引用了DriverManager类,驱动类文件在类被加载的时候调用了静态代码块中的内容,个中就有DriverManager的registerDriver方法,通过这个方法将自己的实例化工具放到了DriverManager的registeredDrivers列表里面,然后当主函数中调用
DriverManager.getConnection()方法的时候就可以从registeredDrivers列表中获取到相应的工具来建立连接了。
虽然理解上述事理并且读了源代码,但是仍旧有疑问: 那种驱动装载办法怎么看都有点怪异,为什么要用这种办法来创建连接呢?经由查找资料创造SPI机制
SPI机制
SPI英文为Service Provider Interface单从字面可以理解为Service供应者接口,正如从SPI的名字去理解SPI便是Service供应者接口;我对SPI的定义:供应给做事供应厂商与扩展框架功能的开拓者利用的接口。
在我们日常开拓的时候都是对问题进行抽象成Api然后就供应各种Api的实现,这些Api的实现都是封装与我们的Jar中或框架中的虽然当我们想要供应一种Api新实现时可以不修正原来代码只需实现该Api就可以供应Api的新实现,但我们还是天生新Jar或框架(虽然可以通过在代码里扫描某个目录已加载Api的新实现,但这不是Java的机制,只是hack方法),而通过Java SPI机制我们就可以在不修正Jar包或框架的时候为Api供应新实现。
很多框架都利用了java的SPI机制,如java.sql.Driver的SPI实现(MySQL驱动、oracle驱动等)、common-logging的日志接口实现、dubbo的扩展实现等等框架;
SPI机制的约定:
在META-INF/services/目录中创建以接口全限定名命名的文件该文件内容为Api详细实现类的全限定名利用ServiceLoader类动态加载META-INF中的实现类如SPI的实现类为Jar则须要放在主程序classPath中Api详细实现类必须有一个不带参数的布局方法综上所述,这么做的缘故原由:
运用SPI机制,起到解耦合的浸染,不同数据库厂商的驱动实现遵照同一个接口规范,可以自由切换反射的性能比较低,在能实现相同目标表现下能不用只管即便不用当时的设计职员在设计上存在一定的毛病,进而产生这种怪异的创建连接办法,因此Class.forName这种办法在java 1.6之后就可以省略不写了。05
后话
Java1.6版本发布于2006年4月在那之后直到现在,打仗到的绝大部分教材和资料在讲述JDBC这部分时候仍旧是利用老的连接办法,或者没有解释不同JDK版本之间的差异,这无论是给学习还是生产利用都造成了困扰,这也给广大编程职员以提示:
在日常事情中,要具备对技能的敏感性和对问题的探究精神,碰着不明白的问题或者不合理的地方,在不影响当前事情的条件下该当只管即便利用各种资源把不清楚的地方弄明白并且做好记录,作为技能的积累和沉淀。
虽然现在多数情形下运用的是更高等的连接池和持久化框架,这些框架都对数据库连接进行了很好的封装,日常开拓中直接应用JDBC连接的情形相对来说比较少,但是JDBC办法的高性能和高可优化性对性能哀求较高的场景下依然是一种比较好的选择,以是还是建议给予足够的重视。