一、意图:java
将抽象部分(抽象接口)与它的实现部分(代码实现)分离,使它们均可以独立地变化。mysql
理解:抽象部分是对外展示的接口(api),而实现部分是针对抽象接口提供的不一样版本的功能实现,使二者独立变化指二者能够在各自的维度上自由变化,而不会产生太大的影响。如能够在api中添加新的接口,而不影响具体实现部分;能够在实现部分针对特定接口添加新的实现方式,而不影响抽象接口定义。sql
桥接模式将类的继承关系转变为类的聚合关系(见下图)。对于抽象接口及其实现,Java中一般的实现方式是经过抽象类的继承或接口的实现,可是桥接模式,将这种抽象接口与实现之间的关系变成了聚合关系。数据库
二、桥接模式(也称:桥梁模式)类图api
角色:app
协做:ide
三、JDBC与桥接:ui
JDBC是桥接模式的典型实现。url
先看下类图:spa
一般使用JDBC链接数据库时,会使用以下代码:
1 Class.forName("数据库类驱动器"); 2 Connection conn = DriverManager.getConnection("数据库url", "用户名", "密码"); 3 //.................
针对不一样的数据库,JDBC均可以经过java.sql.DriverManager类的静态方法getConnection(数据库url, 用户名, 密码)来获取数据库的链接。JDBC经过DriverManager对外提供了操做数据库的统一接口getConnection,经过该方法能够获取不一样数据库的链接,而且经过Connection类提供的接口来进行数据的查询操做。
JDBC为不一样的数据库操做提供了相同的接口,可是JDBC自己并无针对每种数据库提供一套具体实现代码,而是经过接口java.sql.Driver的connect方法链接到了不一样的数据库实现。
1 public interface Driver 2 { 3 4 public abstract Connection connect(String s, Properties properties) throws SQLException; 5 //其余方法 6 7 }
在JDBC中,针对不一样数据库提供的统一的操做接口经过java.sql.Driver(桥)链接到了不一样的数据库实现。如链接mysql数据库。
1 package com.mysql.jdbc; 2 3 public class NonRegisteringDriver implements java.sql.Driver //对java.sql.Driver接口提供了实现 4 { 5 6 public Connection connect(String url, Properties info) 7 throws SQLException 8 { 9 //实现 10 } 11 12 //其余方法 13 }
Java在链接MySQL时须要使用mysql-connector-java.jar,mysql-connector-java.jar包提供了对MySQL数据库操做的具体实现,并经过接口Driver链接到了JDBC统一的api。
四、JDBC中桥接模式具体如何实现?
既然,针对不一样的数据库,经过DriverManger.getConnection()能够得到相同的Connection接口,那先看DriverManager的源码:
1 public class DriverManager 2 { 3 private static final CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList(); //存放DriverInfo的链表 4 5 public static synchronized void registerDriver(Driver driver) 6 throws SQLException 7 { 8 if(driver != null) 9 registeredDrivers.addIfAbsent(new DriverInfo(driver)); //向链表中添加DriverInfo实例,DriverInfo封装了Driver 10 else 11 throw new NullPointerException(); 12 println((new StringBuilder()).append("registerDriver: ").append(driver).toString()); 13 } 14 15 private static Connection getConnection(String s, Properties properties, Class class1) 16 throws SQLException 17 { 18 //..... 19 Iterator iterator = registeredDrivers.iterator(); //遍历registeredDrivers表 20 do 21 { 22 if(!iterator.hasNext()) 23 break; 24 DriverInfo driverinfo = (DriverInfo)iterator.next(); 25 if(isDriverAllowed(driverinfo.driver, classloader)) 26 try 27 { 28 Connection connection = driverinfo.driver.connect(s, properties); //调用Driver接口提供的connect方法来获取Connection对象 29 if(connection != null) 30 { 31 return connection; 32 } 33 } 34 catch(SQLException sqlexception1) 35 { 36 if(sqlexception == null) 37 sqlexception = sqlexception1; 38 } 39 } while(true); 40 } 41 42 //其余方法 43 44 } 45 46
从DriverManager.getConnection()源码可见,方法中遍历了包含DriverInfo实例的表registeredDrivers,经过表中实例driverinfo来获取封装的java.sql.Driver类型的实例,并调用java.sql.Driver接口的connect方法获取到Connection。
【注:DriverInfo是Driver的封装类。由DriverInfo源码可见。
1 class DriverInfo 2 { 3 4 DriverInfo(Driver driver1) 5 { 6 driver = driver1; 7 } 8 9 public boolean equals(Object obj) 10 { 11 return (obj instanceof DriverInfo) && driver == ((DriverInfo)obj).driver; 12 } 13 14 public int hashCode() 15 { 16 return driver.hashCode(); 17 } 18 19 public String toString() 20 { 21 return (new StringBuilder()).append("driver[className=").append(driver).append("]").toString(); 22 } 23 24 final Driver driver; 25 }
】
那么,Driver实例是什么时候注入到DriverManager类的registeredDrivers中的呢?以mysql为例,在每次使用JDBC链接mysql时,都会有下面的调用:
1 Class.forName("com.mysql.jdbc.Driver");
该行代码经过反射加载了com.mysql.jdbc.Driver类(com.mysql.jdbc.Driver类在mysql-connector-java.jar中,而mysql-connector-java.jar是JDBC链接MySQL的jar包),查看com.mysql.jdbc.Driver类:
1 package com.mysql.jdbc; 2 3 public class Driver extends NonRegisteringDriver 4 implements java.sql.Driver 5 { 6 7 public Driver() 8 throws SQLException 9 { 10 } 11 12 static 13 { 14 try 15 { 16 DriverManager.registerDriver(new Driver()); 17 } 18 catch(SQLException E) 19 { 20 throw new RuntimeException("Can't register driver!"); 21 } 22 } 23 }
在com.mysql.jdbc.Driver的源码中能够看到在加载com.mysql.jdbc.Driver类时,经过类中的静态域中的红色代码,会调用DriverManager的registerDriver方法将当前MySQL的驱动类实例注入到DriverManager的registeredDrivers中。
经过整个代码调用,展现了桥接模式在JDBC中是如何运用的。
五、适用性:
六、特色: