zoukankan      html  css  js  c++  java
  • 【设计模式】jdbc桥连接过程解析

    读多少源码,便知自己有多无知!

    想温习一下桥链接模式,然后觉得自己已然吃透了,因为自己写的博客,觉得还是应该更具体一些。

    类似于这样的结构:

    个人理解:    
    模式类型:
    概述:
    角色:
    模式的应用场景:
    结构图:
    模式的优缺点:
    模式的应用实例:
    模式比较:
    代码(其实读UML图要比代码还要一目了然):
    参考/转自:

    转载请注明:

    便是一目了然了,觉得应该找个在Java中桥连接模式的实际应用,于是就找到了Jdbc连接。

    于是,困惑的旅程开始了.......

    [java] view plain copy
     
    1. //加载及注册JDBC驱动程序  
    2. Class.forName("com.mysql.jdbc.Driver");  
    3. Class.forName("com.mysql.jdbc.Driver").newInstance();  
    4.   
    5. Class.forName("oracle.jdbc.driver.OracleDriver")  
    6. Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();  
    7.   
    8. //建立连接对象  
    9. String url="jdbc:mysql://localhost:3306/sample_db?user=root&password=your_password";  
    10. Connection con = DriverManager.getConnection(url);  
    11. //建立SQL陈述式对象(Statement Object)  
    12. Statement stmt = con.createStatement();  
    13. //执行SQL语句  
    14. executeQuery()  
    15. String query = "select * from test";  
    16. ResultSet rs=stmt.executeQuery(query);  
    17. //结果集ResultSet  
    18. while(rs.next())  
    19. {rs.getString(1);rs.getInt(2);}  

    如果用Jdbc连接,只需要用不同的Class.forName参数即可,剩下的都一样。是用oracle还是mysql,只是一个参与的不同而已,然后下面都是一样的。那么问题来了,Class.forName是怎么个用法,干嘛的。为什么有的newInstance了,有的没有,它和DriverManager有啥见不得人的关系?

    1、先搞明白Class.forName再说吧。

    在JDK帮助文档中定义:Class.forName(String className)返回与带有给定字符串名的类或接口相关联的Class对象,
    参数className是所需类的完全限定名;返回值是具有指定名的类的Class对象.如调用Class.forName("x") 将导致名为x的类被初始化.

    简而言之:Class.forName(xxx.xx.xx)返回的是一个类
    Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段

    举个例子:

    A a = (A)Class.forName(“pacage.A”).newInstance();
    这和你 A a = new A(); 是一样的效果。

    new 和Class.forName()有什么区别?
    它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。首先,newInstance( )是一个方法,而new是一个关键字。
    其次,Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用 new关键字生成对象没有这个限制。
    简言之:
    newInstance(): 弱类型,低效率,只能调用无参构造。
    new: 强类型,相对高效,能调用任何public构造。
    Class.forName(“”)返回的是类。
    Class.forName(“”).newInstance()返回的是object 。

    原来Class.forName只是相当于在动态加载类。
    通过查询Java Documentation我们会发现使用Class.forName( )静态方法的目的是为了动态加载类。
    通常编码过程中,在加载完成后,一般还要调用Class下的newInstance( )静态方法来实例化对象以便操作。因此,单单使用Class.forName( )是动态加载类是没有用的,其最终目的是为了实例化对象。

    2、那么问题来了,为什么在加载数据库驱动包的时候有用的是Class.forName( ),却没有调用newInstance( )?

    Class.forName("");的作用是要求JVM查找并加载指定的类,首先要明白,java里面任何class都要装载在虚拟机上才能运行,而静态代码是和class绑定的,class装载成功就表示执行了你的静态代码了,而且以后不会再走这段静态代码了。
    而我们前面也说了,Class.forName(xxx.xx.xx)的作用就是要求JVM查找并加载指定的类,如果在类中有静态初始化器的话,JVM必然会执行该类的静态代码段。
    而在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBC Driver的 Driver类的代码都必须类似如下:

    [java] view plain copy
     
    1. public class MyJDBCDriver implements Driver {  
    2.     static {  
    3.         DriverManager.registerDriver(new MyJDBCDriver());  
    4.     }  
    5. }  


    既然在静态初始化器的中已经进行了注册,所以我们在使用JDBC时只需要Class.forName(XXX.XXX);就可以了。即使写上.newInstance()效果也是一样的。

    相关英文参考文献如下:
    we just want to load the driver to jvm only, but not need to user the instance of driver,
    so call Class.forName(xxx.xx.xx) is enough, if you call Class.forName(xxx.xx.xx).newInstance(),
    the result will same as calling Class.forName(xxx.xx.xx),
    because Class.forName(xxx.xx.xx).newInstance() will load driver first,
    and then create instance, but the instacne you will never use in usual,

    so you need not to create it.

    读完上面的解释,然后想去找了一个oracle驱动ojdbc6.jar,没找到源代码,好吧,那就用mysql-jdbc。5.1.11.jar代替吧。

    以下是源代码:

    [java] view plain copy
     
    1. package com.mysql.jdbc;  
    2. import java.sql.SQLException;  
    3. //源代码地址:http://www.docjar.com/html/api/com/mysql/jdbc/Driver.java.html  
    4. public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
    5.     // ~ Static fields/initializers  
    6.     // ---------------------------------------------  
    7.     //  
    8.     // Register ourselves with the DriverManager  
    9.     //  
    10.     static {  
    11.         try {  
    12.             java.sql.DriverManager.registerDriver(new Driver());  
    13.         } catch (SQLException E) {  
    14.             throw new RuntimeException("Can't register driver!");  
    15.         }  
    16.     }  
    17.     // ~ Constructors  
    18.     // -----------------------------------------------------------  
    19.     /** 
    20.      * Construct a new driver and register it with DriverManager 
    21.      *  
    22.      * @throws SQLException 
    23.      *             if a database error occurs. 
    24.      */  
    25.     public Driver() throws SQLException {  
    26.         // Required for Class.forName().newInstance()  
    27.     }  
    28. }  

    当Class.forName("com.mysql.jdbc.Driver");用的时候,就相当于调用了Driver类中的java.sql.DriverManager.registerDriver(new Driver());,实例化一个Driver,然后再java.sql.DriverManager中进行注册。

    我猜在Oracle的驱动ojdbc6类oracle.jdbc.driver.OracleDriver中也有一段java.sql.DriverManager.registerDriver(new Driver());,把自己注册给DriverManager。

    3、那么下一步就看看DriverManager吧

    [java] view plain copy
     
    1. public class DriverManager {  
    2.   private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();  
    3.   ......  
    4.   static {  
    5.         loadInitialDrivers();  
    6.         println("JDBC DriverManager initialized");  
    7.     }  
    8.   ......  
    9.   public static synchronized void registerDriver(java.sql.Driver driver)  
    10.         throws SQLException {  
    11.   
    12.         /* Register the driver if it has not already been added to our list */  
    13.         if(driver != null) {  
    14.             registeredDrivers.addIfAbsent(new DriverInfo(driver));  
    15.         } else {  
    16.             // This is for compatibility with the original DriverManager  
    17.             throw new NullPointerException();  
    18.         }  
    19.   
    20.         println("registerDriver: " + driver);  
    21.   
    22.     }  
    23.   public static Connection getConnection(String url,  
    24.         String user, String password) throws SQLException {  
    25.         java.util.Properties info = new java.util.Properties();  
    26.         if (user != null) {  
    27.             info.put("user", user);  
    28.         }  
    29.         if (password != null) {  
    30.             info.put("password", password);  
    31.         }  
    32.         return (getConnection(url, info, Reflection.getCallerClass()));  
    33.     }  
    34.   private static Connection getConnection(  
    35.         String url, java.util.Properties info, Class<?> caller) throws SQLException {  
    36.         ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;  
    37.         synchronized (DriverManager.class) {  
    38.             // synchronize loading of the correct classloader.  
    39.             if (callerCL == null) {  
    40.                 callerCL = Thread.currentThread().getContextClassLoader();  
    41.             }  
    42.         }  
    43.     ......  
    44.     for(DriverInfo aDriver : registeredDrivers) {  
    45.             // If the caller does not have permission to load the driver then  
    46.             // skip it.  
    47.             if(isDriverAllowed(aDriver.driver, callerCL)) {  
    48.                 try {  
    49.                     println("    trying " + aDriver.driver.getClass().getName());  
    50.                     Connection con = aDriver.driver.connect(url, info);  
    51.                     if (con != null) {  
    52.                         // Success!  
    53.                         println("getConnection returning " + aDriver.driver.getClass().getName());  
    54.                         return (con);  
    55.                     }  
    56.                 } catch (SQLException ex) {  
    57.                     if (reason == null) {  
    58.                         reason = ex;  
    59.                     }  
    60.                 }  
    61.   
    62.             } else {  
    63.                 println("    skipping: " + aDriver.getClass().getName());  
    64.             }  
    65.   
    66.         }  
    67.   }  
    68. }  

    3.1 Reflection.getCallerClass()获得是谁调用了它的类

    3.2 properties文件是一个文本文件。一般这么用

    [java] view plain copy
     
    1. InputStream is = new FileInputStream("D:\myprojects\lession4\src\stu\ttt.properties");   
    2.   //创建一个Properties容器   
    3.   Properties prop = new Properties();   
    4.   //从流中加载properties文件信息   
    5.   prop.load(is);   
    6.   //循环输出配置信息   
    7.   for (Object key : prop.keySet()) {   
    8.           System.out.println(key + "=" + prop.get(key));   
    9.   }  

    3.3 ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的class对象。

    这一句是重点:registeredDrivers.addIfAbsent(new DriverInfo(driver));

    方法registerDriver中,放driver放到了registeredDrivers中,DriverInfo只是Driver的一个包装类,当看到addIfAbsent,就能猜出来,registerDriver大约是一个list,再看看定义:CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>(); CopyOnWriteArrayList肯定是一个ArrayList吧。然后再看类CopyOnWriteArrayList,

    [java] view plain copy
     
    1. public class CopyOnWriteArrayList<E>  
    2.     implements List<E>, RandomAccess, Cloneable, java.io.Serializable {  
    3.     ......  
    4. }  

    3.4 接口RandomAccess是干嘛的,有点让我怀疑,CopyOnWriteArrayList是不是一个ArrayList了,不过肯定和ArrayList有关系就是了,要不然不会这么起名。

    查一下JDK,解释如下:

    public interface RandomAccess

    List 实现所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。

    将操作随机访问列表的最佳算法(如 ArrayList)应用到连续访问列表(如 LinkedList)时,可产生二次项的行为。如果将某个算法应用到连续访问列表,那么在应用可能提供较差性能的算法前,鼓励使用一般的列表算法检查给定列表是否为此接口的一个instanceof,如果需要保证可接受的性能,还可以更改其行为。

    现在已经认识到,随机和连续访问之间的区别通常是模糊的。例如,如果列表很大时,某些 List 实现提供渐进的线性访问时间,但实际上是固定的访问时间。这样的List 实现通常应该实现此接口。实际经验证明,如果是下列情况,则List实现应该实现此接口,即对于典型的类实例而言,此循环:

         for (int i=0, n=list.size(); i < n; i++)
             list.get(i);
     

    的运行速度要快于以下循环:

         for (Iterator i=list.iterator(); i.hasNext(); )
             i.next();
     

    强调:
    JDK中推荐的是对List集合尽量要实现RandomAccess接口
    如果集合类是RandomAccess的实现,则尽量用for(int i = 0; i < size; i++) 来遍历而不要用Iterator迭代器来遍历,在效率上要差一些。反过来,如果List是Sequence List,则最好用迭代器来进行迭代。

    JDK中说的很清楚,在对List特别是Huge size的List的遍历算法中,要尽量来判断是属于RandomAccess(如ArrayList)还是Sequence List (如LinkedList),因为适合RandomAccess List的遍历算法,用在Sequence List上就差别很大,常用的作法就是:
        要作一个判断:
      

    [java] view plain copy
     
    1. if (list instance of RandomAccess) {  
    2.         for(int m = 0; m < list.size(); m++){}  
    3.     }else{  
    4.         Iterator iter = list.iterator();  
    5.         while(iter.hasNext()){}  
    6.     }  

    一说到这里,就又想到一设计模式,迭代模式,看到要改再写了......

    好吧,现在搞清楚了RandomAccess接口。原来list.size(),和iterater.hasNext()遍历是不一样的啊!!

    搞明白了RandomAccess接口,就明白了registeredDrivers,那么谁用到了registeredDrivers呢?

    4、是方法getConnection用到了registeredDrivers啊,通过遍历for(DriverInfo aDriver : registeredDrivers),然后创建Connection con = aDriver.driver.connect(url, info);最后返回一个con,这样就和Connection con = DriverManager.getConnection(url);连起来啦

    这就是顺了吧,Class.forName("com.mysql.jdbc.Driver")之后用Connection con = DriverManager.getConnection(url)就知道为什么了吧。

    那么Jdbc桥连接是怎么应用的呢?

    5、因为刚学完Class.forName会加载静态代码块,看DriverManager时,就看到一个静态代码块,里面写着loadInitialDrivers();那就点进去看看吧。看到这么一段代码,感觉瞬间就高逼格了,靠,虽然在用富客户端JavaFx2的时候,也是写匿名类,形式和这个差不多,但没有深思过。

    [java] view plain copy
     
    1. AccessController.doPrivileged(new PrivilegedAction<Void>() {  
    2.             public Void run() {  
    3.                 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);  
    4.                 Iterator driversIterator = loadedDrivers.iterator();  
    5.                 try{  
    6.                     while(driversIterator.hasNext()) {  
    7.                         driversIterator.next();  
    8.                     }  
    9.                 } catch(Throwable t) {  
    10.                 // Do nothing  
    11.                 }  
    12.                 return null;  
    13.             }  
    14.         });  

    PrivilegedAction类:

    [java] view plain copy
     
    1. public interface PrivilegedAction<T> {      
    2.     T run();  
    3. }  

    注意到AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() {}} 没,貌似是在New接口啊!!!!

    5.1然后查了一,解释如下:

    new一个对象的时候,相当于new了一个实现该接口的类。相当于动态的实现了接口。

    [java] view plain copy
     
    1. <span style="font-size:14px;">//按照书本来说,接口是不可以被new的,但你要new一个也可以这样用。就像下面的代码:  
    2. //例1  
    3. Runnable= new Runnable() {  
    4.   public void run() {  
    5.     //.....  
    6.   }  
    7. });  
    8. //例2  
    9. Thread thread = new Thread(new Runnable() {  
    10.     @Override  
    11.     public void run() {  
    12.         //...  
    13.     }  
    14. });  
    15. //例3  
    16. //比如 一个接口 A ,里面有一个方法fun1(),一般我们是先定义它的实现再引用它,比如  
    17. public class ImpA implements A{  
    18.  public void fun1(){  
    19.     //do some thing...  
    20.  }  
    21. }  
    22. //然后在另一个类调用  
    23. public class Class1 {  
    24.  public void method1(){  
    25.     A a = new ImpA();  
    26.  }  
    27. }  
    28. //<span style="color:#FF0000;">但有时我想对这个接口做另一个实现不想使用ImpA,但由于是临时的又不想去做定义,则你就可以这样</span>  
    29. public class Class1 {  
    30.  public void method1(){  
    31.     A a = new A(){ //相当于在内存中直接产生了一个接口的实现并引用这个内存对象。动态的代码  
    32.        public void fun1(){  
    33.          //do some thing...  
    34.        }  
    35.     };  
    36.  }  
    37. }</span>  


    匿名类是个小插件,咱们继续...

    点进去AccessController的方法:public static native <T> T doPrivileged(PrivilegedAction<T> action);

    看到一个关键字:native

    5.2 那么native是干嘛的?

     简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。
       "A native method is a Java method whose implementation is provided by non-java code."
       在定义一个native method时,并不提供实现体(有些像定义一个java interface),因为其实现体是由非java语言在外面实现的。

    6、ReentrantLock是干嘛的

    在看到registeredDrivers.addIfAbsent(new DriverInfo(driver));的时候,点进去了,看了一下addIfAbsent的方法,一进去,就看到这么2行:

    [java] view plain copy
     
    1. public boolean addIfAbsent(E e) {  
    2.         final ReentrantLock lock = this.lock;  
    3.         lock.lock();  
    4.         try {  
    5.             .......  
    6.             return true;  
    7.         } finally {  
    8.             lock.unlock();  
    9.         }  
    10.     }  

    ReentrantLock是干嘛的,看起来很吊的样子,一看肯定是上锁的嘛,那为什么不用synchronized呢?

    一查,吓一跳,看起来很复杂的样子,而且非常高大尚

    对 synchronized 的改进
    如此看来同步相当好了,是么?那么为什么 JSR 166 小组花了这么多时间来开发 java.util.concurrent.lock 框架呢?答案很简单-同步是不错,但它并不完美。它有一些功能性的限制 —— 它无法中断一个正在等候获得锁的线程,也无法通过投票得到锁,如果不想等下去,也就没法得到锁。同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。

    ReentrantLock 类
    java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。 ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)

    reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。

    Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。

    再对比一下原博文,一般情况下还是用synchronized,它还是很好用的。

    ------------------------------------   结论的分割线   ------------------------------------

    7、绕了这么大一圈,那么Jdbc和桥连接是怎么应用的呢? 没有一个抽象类是持有持有一个接口,所以也不存在抽象和现实分离啊?

    搜索了一下,在网上看到这么一个解释:

    我们写的应用程序,是面向JDBC的API在开发,这些接口就相当于桥接模式中的抽象部分的接口。那么怎样得到这些API的呢?是通过DriverManager来得到的。此时的系统结构如图9所示:



     图9  基于JDBC开发的应用程序结构示意图
     

            那么这些JDBC的API,谁去实现呢?光有接口,没有实现也不行啊。
            该驱动程序登场了,JDBC的驱动程序实现了JDBC的API,驱动程序就相当于桥接模式中的具体实现部分。而且不同的数据库,由于数据库实现不一样,可执行的Sql也不完全一样,因此对于JDBC驱动的实现也是不一样的,也就是不同的数据库会有不同的驱动实现。此时驱动程序这边的程序结构如图10所示:


               图10  驱动程序实现结构示意图
              

            有了抽象部分——JDBC的API,有了具体实现部分——驱动程序,那么它们如何连接起来呢?就是如何桥接呢?
            就是前面提到的DriverManager来把它们桥接起来,从某个侧面来看,DriverManager在这里起到了类似于简单工厂的功能,基于JDBC的应用程序需要使用JDBC的API,如何得到呢?就通过DriverManager来获取相应的对象。
            那么此时系统的整体结构如图11所示:


                                   图11  JDBC的结构示意图
     

            通过上图可以看出,基于JDBC的应用程序,使用JDBC的API,相当于是对数据库操作的抽象的扩展,算作桥接模式的抽象部分;而具体的接口实现是由驱动来完成的,驱动这边自然就相当于桥接模式的实现部分了。而桥接的方式,不再是让抽象部分持有实现部分,而是采用了类似于工厂的做法,通过DriverManager来把抽象部分和实现部分对接起来,从而实现抽象部分和实现部分解耦。 

            JDBC的这种架构,把抽象和具体分离开来,从而使得抽象和具体部分都可以独立扩展。对于应用程序而言,只要选用不同的驱动,就可以让程序操作不同的数据库,而无需更改应用程序,从而实现在不同的数据库上移植;对于驱动程序而言,为数据库实现不同的驱动程序,并不会影响应用程序。而且,JDBC的这种架构,还合理的划分了应用程序开发人员和驱动程序开发人员的边界。
            对于有些朋友会认为,从局部来看,体现了策略模式,比如在上面的结构中去掉“JDBC的API和基于JDBC的应用程序”这边,那么剩下的部分,看起来就是一个策略模式的体现。此时的DriverManager就相当于上下文,而各个具体驱动的实现就相当于是具体的策略实现,这个理解也不算错,但是在这里看来,这么理解是比较片面的。
            对于这个问题,再次强调一点:对于设计模式,要从整体结构上、从本质目标上、从思想体现上来把握,而不要从局部、从表现、从特例实现上来把握。

    =====================================================================

    ========================  个人的理解   ==================================

    ======================================================================

    在源代码中:Connection con = aDriver.driver.connect(url, info); 这句话是重点,告诉我们 Connection是怎么来的。aDriver是从registeredDrivers来的,而Class.forName("com.mysql.jdbc.Driver");时,New了一个Driver注册给registeredDrivers的,此时再用driver.connect返回一个Connection的实例,这是一个回调!

    Class.forName是用Mysql还是Oracle,这个Driver一定会实现接口java.sql.Driver,然后通过DriverManager.registerDriver(new Driver());使DriverManager类持有一个Driver,是否可以把DriverManager当成桥,当成桥连接中的抽象类?然后持有一个接口Driver,至于是Mysql还是Oracle,不关心,坐等传参。因为DriverManager持有的是一个Driver接口,你传过来什么,我就得到什么的实例化,然后我再通过getConnection用你的实例,去调用你自己的方法connect,去获得Connection的一个实例。

    看到没,这就是接口的威力呀!

    我定义接口,接口中我定义自己得到实例的抽象方法,最后我在自己的类中去调用这个方法去得到底层的实例。无论是oracle,还是Mysql,还是其它的广商,你要实现我定义的接口。

    结果是,大家按标准来,我定义上层的东西,然后获得了底层提供的东西,代码不用动,只是参了一个不同的参数,就可以获得不同的实例,至于底层是怎么实现的,我可以做到完全不关心。

    下面贴一下代码,再顺一下流程:

    java.sql.Driver类

    [java] view plain copy
     
    1. package java.sql;  
    2. import java.util.logging.Logger;  
    3.   
    4. public interface Driver {  
    5.     Connection connect(String url, java.util.Properties info)  
    6.         throws SQLException;  
    7.     boolean acceptsURL(String url) throws SQLException;  
    8.     DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)  
    9.                          throws SQLException;  
    10.     int getMajorVersion();  
    11.     int getMinorVersion();  
    12.     boolean jdbcCompliant();  
    13.     public Logger getParentLogger() throws SQLFeatureNotSupportedException;  
    14. }</span>  

    com.mysql.Driver类

    [java] view plain copy
     
    1. <span style="font-size:14px;">package com.mysql.jdbc;  
    2. import java.sql.SQLException;  
    3.   
    4. public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
    5.   
    6.     static {  
    7.         try {  
    8.             java.sql.DriverManager.registerDriver(new Driver());  
    9.         } catch (SQLException E) {  
    10.             throw new RuntimeException("Can't register driver!");  
    11.         }  
    12.     }  
    13.   
    14.     public Driver() throws SQLException {  
    15.         // Required for Class.forName().newInstance()  
    16.     }  
    17. }  


    既然是com.mysql.jdbc.Driver实现,为什么没有实现 Driver的connect方法呢?原因是NonRegisteringDriver,它实现了java.sql.Driver接口,所以Driver就不用实现它了,Driver的目的只是为了注册,而去创建一个Connection则是由NonRegisteringDriver去完成的。最后贴一部分NonRegisteringDriver代码:

    com.mysql.NonRegisteringDriver类(http://www.docjar.com/html/api/com/mysql/jdbc/NonRegisteringDriver.java.html)

    [java] view plain copy
     
    1.  public class NonRegisteringDriver implements java.sql.Driver {  
    2.   
    3.      public java.sql.Connection connect(String url, Properties info)  
    4.             throws SQLException {  
    5.         ......  
    6.         try {  
    7.             Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(  
    8.                     host(props), port(props), props, database(props), url);  
    9.             return newConn;  
    10.         } catch (SQLException sqlEx) {  
    11.             .....  
    12.             throw sqlEx;  
    13.         } catch (Exception ex) {  
    14.             .......  
    15.             throw sqlEx;  
    16.         }  
    17.     }  
    18. }  

    java.sql.DriverManager类

    [java] view plain copy
     
    1. public class DriverManager {  
    2.     public static synchronized void registerDriver(java.sql.Driver driver)  
    3.         throws SQLException {  
    4.          .......  
    5.          registeredDrivers.addIfAbsent(new DriverInfo(driver));  
    6.          .....  
    7.     }  
    8.     private static Connection getConnection(  
    9.         String url, java.util.Properties info, Class<?> caller) throws SQLException {  
    10.          .......  
    11.         for(DriverInfo aDriver : registeredDrivers) {  
    12.             // If the caller does not have permission to load the driver then  
    13.             // skip it.  
    14.             if(isDriverAllowed(aDriver.driver, callerCL)) {  
    15.                 try {  
    16.                     //此处进行了回调  
    17.                     Connection con = aDriver.driver.connect(url, info);  
    18.          .....  
    19.     }  
    20. }  



    PS:

    不允许类多重继承的主要原因是,如果A同时继承B和C,而b和c同时有一个D方法,A如何决定该继承那一个呢?
    但接口不存在这样的问题,接口全都是抽象方法继承谁都无所谓,所以接口可以继承多个接口。一个类如果实现了一个借口,则要实现该接口的所有方法。而接口则不用实现继承接口的方法。

    看源代码,能涉及到这么多的知识点,路漫漫.......

    参考:

    jdbc的Class.forName解释1:http://blog.csdn.net/jackterq/article/details/4480745

    jdbc的Class.forName解释2:http://blog.csdn.net/kaiwii/article/details/7405761

    参考RandomAccess:http://blog.csdn.net/keda8997110/article/details/8635005

    有关匿名类:http://bbs.csdn.net/topics/390133247

    native参考:http://blog.csdn.net/wike163/article/details/6635321

    ReentrantLock和synchronized两种锁定机制的对比:http://blog.csdn.net/fw0124/article/details/6672522

    研磨设计模式之桥接模式 http://chjavach.iteye.com/blog/750381

    http://www.docjar.com/html/api/com/mysql/jdbc/NonRegisteringDriver.java.html

    http://www.docjar.com/html/api/com/mysql/jdbc/Driver.java.html

        创建型模式,共五种:工厂方法模式、抽象工厂模式单例模式建造者模式原型模式
    结构型模式,共七种:适配器模式装饰器模式代理模式外观模式桥接模式组合模式享元模式
        行为型模式,共十一种:策略模式模板方法模式观察者模式迭代子模式责任链模式命令模式备忘录模式状态模式访问者模式中介者模式解释器模式

        补充模式:空对象模式

    转载请注明:http://blog.csdn.net/paincupid/article/details/43614029

    参考资料:

    http://blog.csdn.net/paincupid/article/details/43614029

  • 相关阅读:
    Sharepoint 2010 无法上传文件的问题
    复杂领域的Cynefin模型和Stacey模型
    SCOM Visio监控 与sharepoint 2010 整合
    HillStone上网认证客户端
    jQuery插件手把手教会(二)
    jQuery插件手把手教会(一)
    jQuery+css+div--一些细节详解
    jQuery+css+div一些值得注意的常用语句
    找不到对应的webservice配置参数[ProcessService]
    NC保存报dirty解决方法
  • 原文地址:https://www.cnblogs.com/junneyang/p/8555450.html
Copyright © 2011-2022 走看看