JDBC/DriverManager原理--注册驱动

此处只讨论 加载驱动的问题,其他无关的问题我们暂时忽略

疑问

一般使用jdbc 连接数据库,手写代码如下

//jdk6之后,不需示执行Class.forName
Class.forName("oracle.jdbc.driver.OracleDriver");

Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","xxx","xxxx");


我第一次写此代码时,就有很多疑问,但是后来也没研究,今天我把当时的疑问列出

1  "oracle.jdbc.driver.OracleDriver"  和"jdbc:oracle:thin"  有什么关系,为什么要写两遍

2  Class.forName 注册驱动后,没有返回值,是怎么回事

3  Class.forName  为什么 此行在jdk6版本中 又不需要了呢

4 DriverManager.getConnection怎么使用的驱动的呢

分析源码

要回答上边的问题,我们先来看看这些代码的源码

1 要使用jdbc连接 oracle,必须先加载驱动,Class.forName就是加载驱动
Class.forName会初始化 oracle.jdbc.driver.OracleDriver这个类 (关于类的加载过程可自行搜索),初始化时自动执行类的静态代码块
// oracle.jdbc.driver.OracleDriver
static{
...
 if (defaultDriver == null) {
                defaultDriver = new oracle.jdbc.OracleDriver();
                DriverManager.registerDriver(defaultDriver);
  }

这样就驱动就注册了

2 jdk6为什么不需要 执行了Class.forName这行代码了呢

大家先了解一下ServiceLoader 。

简单来说就是在驱动jar包配置文件中,指定oracle.jdbc.driver.OracleDriver实现java.sql.Driver接口;然后DriverManager在初始化的时候,自动扫描所有jar包中实现了java.sql.Driver的类 ,并初始化 此实现类

//DriverManager
   static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
    private static void loadInitialDrivers() {
       ...
        
        AccessController.doPrivileged(new PrivilegedAction() {
            public Void run() {
                //搜索服务的实现类(驱动实例)
                ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator driversIterator = loadedDrivers.iterator();
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();// 此处初始化  并注册了
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;


//ServiceLoader
       public S next() {
            if (acc == null) {
                return nextService();
            } else {
        ...

//ServiceLoader
       private S nextService() {
            ...
            try {
                c = Class.forName(cn, false, loader);
            ...

 也就是说 DriverManger借助 ServiceLoader 找到驱动 并注册了,所以不需要再手工注册

 

3 驱动注册了,DriverManager中 驱动怎么被使用呢

//caller = Reflection.getCallerClass()
private static Connection getConnection(
        String url, java.util.Properties info, Class caller) throws SQLException {
         ....
        
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }
        //callerCL 是方法调用类或者当前线程的classLoader,会在isDriverAllowed中使用
        ...
        // 所有已经注册驱动都保存在registeredDrivers,这是个CopyOnWriteArrayList
        for(DriverInfo aDriver : registeredDrivers) {
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    //jdbc:oracle:thin:@localhost:1521:orcl 最终是由驱动实现类使用
                    Connection con = aDriver.driver.connect(url, info);
        ...




  private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
        boolean result = false;
        if(driver != null) {
            Class aClass = null;
            try {
                //使用不同的classLoader加载出来的驱动类是不相等的,此处就利用这点判断权限
                aClass =  Class.forName(driver.getClass().getName(), true, classLoader);
            } catch (Exception ex) {
                result = false;
            }

             result = ( aClass == driver.getClass() ) ? true : false;
        }

        return result;
    }

遍历 registeredDrivers(所有已经注册驱动都在这里), 使用 DriverManager.getConnection方法所在类的classLoader(如果为空,则使用当前线程上下文的classLoaer),去加载驱动类  ,然后和registeredDrivers里边的比较 ,如果相等,则此驱动是有权限被 使用。

注意:此处 遍历 registeredDrivers时,只要找到一个有权限的,就立即返回。

如果使用相同方式注册多个了驱动,调用的是哪个驱动呢? registeredDrivers中第一个有权限且能正确连接上的的 ;那么程序里边 如果要连接多个数据库,使用jdbc怎么操作?

 

 

你可能感兴趣的:(j2se)