参考Mysql JDBC 驱动源码实现自定义驱动

一、分析Mysql JDBC

平常我们直接以JDBC连接数据库代码基本都如下:

    Connection con = null;  //创建用于连接数据库的Connection对象  
        try {  
            Class.forName("com.mysql.jdbc.Driver");// 加载Mysql数据驱动  
              
            con = DriverManager.getConnection(  
                    "jdbc:mysql://localhost:3306/myuser", "root", "root");// 创建数据连接  
              
        } catch (Exception e) {  
            System.out.println("数据库连接失败" + e.getMessage());  
        }  
        return con; //返回所建立的数据库连接  

 

1、我们使用Mysql JDBC的时候需要先注册加载驱动:

 

Class.forName("com.mysql.jdbc.Driver"); 

 

 利用jdk反射机制的真实注册过程如下:

package com.mysql.jdbc;  
  
import java.sql.SQLException;  
public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
      
       //执行这个静态代码块  
    static {  
        try {//注册mysql实现的驱动类  
            java.sql.DriverManager.registerDriver(new Driver());  
        } catch (SQLException E) {  
            throw new RuntimeException("Can't register driver!");  
        }  
    }  
  
    public Driver() throws SQLException {  
        // Required for Class.forName().newInstance()  
    }  
}  

 接着我们进入java.sql.DriverManager.registerDirver(new Driver())源码:

    /**
     * Registers the given driver with the <code>DriverManager</code>.
     * A newly-loaded driver class should call
     * the method <code>registerDriver</code> to make itself
     * known to the <code>DriverManager</code>.
     *
     * @param driver the new JDBC Driver that is to be registered with the
     *               <code>DriverManager</code>
     * @exception SQLException if a database access error occurs
     */
    public static synchronized void registerDriver(java.sql.Driver driver)
	throws SQLException {
	if (!initialized) {
	    initialize();//初始化
	}
      
	DriverInfo di = new DriverInfo();//存储驱动器信息
	di.driver = driver;
	di.driverClass = driver.getClass();
	di.driverClassName = di.driverClass.getName();
	drivers.addElement(di);//注册到驱动集合
	println("registerDriver: " + di);
    }

 再进行一些细节的初始化工作驱动就注册加载完成了,有兴趣可自己看源码。接着我们如何创建连接。

 

2、创建数据连接

 

Drivermanager.getConnection(url,name,password)

 我们进去看看这个方法的实际执行过程

    public static synchronized Connection getConnection(String url, 
	String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        // Gets the classloader of the code that called this method, may 
	// be null.
	ClassLoader callerCL = DriverManager.getCallerClassLoader();

	if (user != null) {
	    info.put("user", user);//获取用户名
	}
	if (password != null) {
	    info.put("password", password);//获取密码
	}

        return (getConnection(url, info, callerCL));//①
    }
 接着我们再看①处的代码执行过程
    //  Worker method called by the public getConnection() methods.
    private static synchronized Connection getConnection(
	String url, java.util.Properties info, ClassLoader callerCL) 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.
	 */
	if(callerCL == null) {
	    callerCL = Thread.currentThread().getContextClassLoader();
	}    
	  
	if(url == null) {
	    throw new SQLException("The url cannot be null", "08001");
	}
    
	println("DriverManager.getConnection(\"" + url + "\")");
    
	if (!initialized) {
	    initialize();
	}

	// Walk through the loaded drivers attempting to make a connection.
	// Remember the first exception that gets raised so we can reraise it.
	SQLException reason = null;
	for (int i = 0; i < drivers.size(); i++) {//遍历驱动集合
	    DriverInfo di = (DriverInfo)drivers.elementAt(i);
      
	    // If the caller does not have permission to load the driver then 
	    // skip it.
	    if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {
		println("    skipping: " + di);
		continue;
	    }
	    try {
		println("    trying " + di);
		//在注册的驱动集合中得到相应的连接 ①
		Connection result = di.driver.connect(url, info);
		if (result != null) {
		    // Success!
		    println("getConnection returning " + di);
		    return (result);
		}
	    } catch (SQLException ex) {
		if (reason == null) {
		    reason = ex;
		}
	    }
	}
 所以上面的①处的代码实际执行的就是com.mysql.jdbc.Driver的connect(url,info)方法
public java.sql.Connection connect(String url, Properties info)  
            throws SQLException {  
        if (url != null) {  
            if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) {//负载均衡的配置  
                return connectLoadBalanced(url, info);  
            } else if (StringUtils.startsWithIgnoreCase(url,  
                    REPLICATION_URL_PREFIX)) {//复制  
                return connectReplicationConnection(url, info);  
            }  
        }  
  
        Properties props = null;  
  
        if ((props = parseURL(url, info)) == null) {  
            return null;  
        }  
  
        if (!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))) {  
            return connectFailover(url, info);  
        }  
          
        try {//初始化链接  
            Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(  
                    host(props), port(props), props, database(props), url);  
  
            return newConn;  
        } catch (SQLException sqlEx) {  
            // Don't wrap SQLExceptions, throw  
            // them un-changed.  
            throw sqlEx;  
        } catch (Exception ex) {  
            SQLException sqlEx = SQLError.createSQLException(Messages  
                    .getString("NonRegisteringDriver.17") //$NON-NLS-1$  
                    + ex.toString()  
                    + Messages.getString("NonRegisteringDriver.18"), //$NON-NLS-1$  
                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null);  
              
            sqlEx.initCause(ex);  
              
            throw sqlEx;  
        }  
    }  
二、自定义驱动  
		Class.forName("org.tinygroup.dbrouterjdbc3.jdbc.TinyDriver");
		conn = DriverManager.getConnection("jdbc:dbrouter://aggregate", "ljf",
				"123456");
 1、注册加载驱动
public class TinyDriver implements Driver {

    private RouterManager manager;

    private Logger logger = LoggerFactory.getLogger(TinyDriver.class);

    static {
        try {
            DriverManager.registerDriver(new TinyDriver());//注册驱动
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    public TinyDriver() {
        manager = RouterManagerBeanFactory.getManager();
    }
 我们再来看看如何获得数据连接
    public Connection connect(String url, Properties info) throws SQLException {
        if (!acceptsURL(url)) {
            return null;
        }
        String routerName = url.substring("jdbc:dbrouter://".length());
        Router router = manager.getRouter(routerName);
        String user = info.getProperty("user");
        String password = info.getProperty("password");
        if (!user.equals(router.getUserName())) {
            logger.logMessage(LogLevel.ERROR,
                    "username {0} and {1} not equals", user,
                    router.getUserName());
            throw new SQLException("username not equals");
        }
        if (!password.equals(router.getPassword())) {
            logger.logMessage(LogLevel.ERROR,
                    "password {0} and {1} not equals", password,
                    router.getPassword());
            throw new SQLException("password not equals");
        }
        return new TinyConnection(routerName);
    }
 通过以上的分析我们可以通过对JDBC进行包装就能做到多数据源,甚至可以在JDBC层上进行分库分表。这样做的好处是对于上层开发人员是透明的。

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值