点击量:477
如果你使用过Spring管理过数据库连接,那么一般会在applicationContext.xml文件里看到如下的代码:
1 2 3 4 5 6 7 8 |
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>com.goooooogle</value> </property> </bean> |
这段配置就是用来指定datasource的,其实跟普通的bean注入没什么区别啊,那在这背后Spring到底干了些什么呢?这个bean是如何管理数据库连接的呢?想要理解这背后的一切自然就要看看这个类:org.springframework.jdbc.datasource.DriverManagerDataSource到底干了些什么。
1 2 3 4 5 6 7 8 9 |
public class DriverManagerDataSource extends AbstractDriverBasedDataSource{ //........... protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException { return DriverManager.getConnection(url, props); } //........... } |
看到这里应该很清楚了,DriverManagerDataSource这个类只不过非常简单地封装了一下JDK的DriverManager,本质上还是使用DriverManager来获取数据库连接。值得注意的是,DriverManagerDataSource最顶层继承了JDK的DataSource,也就是说只要有一个DataSource的实例,然后调用getConnection()即可。那么JDK的DriverManager又是如何工作的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
// Worker method called by the public getConnection() methods. private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { /* * When callerCl is null, we should check the application's * (which is invoking this class indirectly) * classloader, so that the JDBC driver class outside rt.jar * can be loaded from here. */ ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection(\"" + url + "\")"); // Walk through the loaded registeredDrivers attempting to make a connection. // Remember the first exception that gets raised so we can reraise it. SQLException reason = null; for(DriverInfo aDriver : registeredDrivers) { // If the caller does not have permission to load the driver then // skip it. if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } // if we got here nobody could connect. if (reason != null) { println("getConnection failed: " + reason); throw reason; } println("getConnection: no suitable driver found for "+ url); throw new SQLException("No suitable driver found for "+ url, "08001"); } |
简单解读下这段代码:DriverManager顾名思义就是用来管理数据库驱动(driver)的,他通过registeredDrivers来维护多个数据库驱动,用户只要用一行代码即可完成注册
1 |
DriverManager.registerDriver(new Driver()); |
那么DriverManager是如何知道该用哪个driver的connection呢?DriverManager并不知道,他是对所有的registeredDrivers进行迭代遍历,依次寻找不为空的connection,找到就返回。所以一个driver的connect方法要对传进来的参数做检查判断,以确定是否应该返回。比如不同的数据库驱动可以配置不同的URL,然后只要检查URL是否符合条件就行了。
下面用一副图来解释他们之间的调用和继承关系:
如何自己写一个dbpool?
看懂了上图各种类之间的继承和调用关系,要自己写一个dbpool就不会无的放矢了。我们可以在driver那边做文章,在DriverManager里注册一个dbpoolDriver用于给Spring访问(其实就是把datasource的属性driverClassName配置成dbpoolDriver),然后判断pool里面是否有可用连接,如果有则返回,否则再调用真正的Mysql Driver来创建一个新的连接,也就是说这个dbpoolDriver其实只是个代理层,并没有真正去实现driver的功能。因为registeredDrivers是静态变量,全局共享,所以此时就会有两个driver:dbpool Driver和MySQL Driver,但正如我们之前所说可以通过不同的参数配置来区分这两个driver,也因此避免了spring可能会绕过dbpool而直接调用MySQL Driver的问题。