欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java的JDBC和橋接模式詳解

 更新時(shí)間:2021年09月16日 14:41:06   作者:枯落  
下面小編就為大家?guī)?lái)一篇Java的JDBC和橋接模式(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

本文參考

網(wǎng)上對(duì)于JDBC與橋接模式的理解各有不同,在這片文章里提出的是我個(gè)人對(duì)于二者的理解,本文參考的其它博文如下:

http://www.dbjr.com.cn/article/217763.htm

http://www.dbjr.com.cn/article/112714.htm

http://www.dbjr.com.cn/article/137835.htm

http://edu.jb51.net/design-pattern/design-pattern-bridge.html

橋接模式的定義與特點(diǎn)

定義:

將抽象與實(shí)現(xiàn)分離,使它們可以獨(dú)立變化。它是用組合/聚合關(guān)系代替繼承關(guān)系來(lái)實(shí)現(xiàn),從而降低了抽象和實(shí)現(xiàn)這兩個(gè)可變維度的耦合度。例如針對(duì)一個(gè)圖形,我們可以設(shè)計(jì)顏色和形狀兩個(gè)變化維度

優(yōu)點(diǎn):

由于抽象與實(shí)現(xiàn)分離,所以擴(kuò)展能力強(qiáng);實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶(hù)透明

缺點(diǎn):

由于聚合關(guān)系建立在抽象層,要求開(kāi)發(fā)者針對(duì)抽象化進(jìn)行設(shè)計(jì)與編程,這增加了系統(tǒng)的理解與設(shè)計(jì)難度

橋接模式的基本結(jié)構(gòu)

Abstraction — 抽象化角色:

定義抽象的接口,包含一個(gè)對(duì)實(shí)現(xiàn)化角色的引用

Refined Abstraciotn — 擴(kuò)展抽象化角色:

抽象化角色的子類(lèi),實(shí)現(xiàn)父類(lèi)中的業(yè)務(wù)方法,并通過(guò)組合/聚合關(guān)系調(diào)用實(shí)現(xiàn)化角色中的業(yè)務(wù)方法

Implementor — 實(shí)現(xiàn)化角色:

定義具體行為、具體特征的應(yīng)用接口,供擴(kuò)展抽象化角色使用

Concrete Implemetor — 具體實(shí)現(xiàn)化角色

實(shí)現(xiàn)化角色的具體實(shí)現(xiàn)

基本的模式結(jié)構(gòu)類(lèi)圖如下:

橋接模式的應(yīng)用場(chǎng)景

  • 當(dāng)一個(gè)類(lèi)存在兩個(gè)獨(dú)立變化的維度,且這兩個(gè)維度都需要進(jìn)行擴(kuò)展時(shí)
  • 當(dāng)一個(gè)系統(tǒng)不希望使用繼承或因?yàn)槎鄬哟卫^承導(dǎo)致系統(tǒng)類(lèi)的個(gè)數(shù)急劇增加時(shí)
  • 當(dāng)一個(gè)系統(tǒng)需要在構(gòu)件的抽象化角色和具體化角色之間增加更多的靈活性時(shí)

JDBC源碼剖析

在不使用Spring、Hibernate等第三方庫(kù)的情況下,直接通過(guò)原生JDBC API連接MySQL數(shù)據(jù)庫(kù),則有如下示例代碼:

Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://<host>:<port>/<database>");

短短兩行代碼難以看出橋接模式的結(jié)構(gòu),下面先對(duì)源碼進(jìn)行一定的分析,理解各個(gè)類(lèi)和接口之間的關(guān)系

Class.forName()方法

該方法將返回與給定字符串名的類(lèi)或接口相關(guān)聯(lián)的java.lang.Class類(lèi)對(duì)象,用于在程序運(yùn)行時(shí)的某個(gè)時(shí)刻,由客戶(hù)端調(diào)用,動(dòng)態(tài)加載該類(lèi)或該接口到當(dāng)前線(xiàn)程中

Returns the Class object associated with the class or interface with the given string name.
Given the fully qualified name for a class or interface this method attempts to locate, load, and link the class or interface.

若Class.forName()加載的是一個(gè)類(lèi),也會(huì)執(zhí)行類(lèi)中包含的static { } 靜態(tài)代碼段

com.mysql.cj.jdbc.Driver類(lèi)

MySQL將具體的java.sql.Driver接口的實(shí)現(xiàn)放到了NonRegisteringDriver中,com.mysql.cj.jdbc.Driver類(lèi)僅包含一段靜態(tài)代碼,具體類(lèi)圖如下:

其中最關(guān)鍵的是靜態(tài)代碼段中的 DriverManager.registerDriver(new Driver()) ,它會(huì)在客戶(hù)端調(diào)用Class.forName()方法加載com.mysql.cj.jdbc.Driver類(lèi)的同時(shí)被執(zhí)行,Driver類(lèi)自身的一個(gè)實(shí)例被注冊(cè)到DriverManager(即保存到DriverManager的靜態(tài)字段registeredDrivers內(nèi)),注冊(cè)過(guò)程的源碼如下:

public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)
throws SQLException {
  /* Register the driver if it has not already been added to our list */
  if(driver != null) {
    registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
  } else {
    // This is for compatibility with the original DriverManager
    throw new NullPointerException();
  }
  println("registerDriver: " + driver);
}

registeredDrivers靜態(tài)字段的類(lèi)型是實(shí)現(xiàn)了List接口的CopyOnWriteArrayList類(lèi),它能夠保存進(jìn)一步封裝java.sql.Driver接口的DriverInfo類(lèi)實(shí)例,DriverInfo類(lèi)的聲明代碼如下:

class DriverInfo {
  final Driver driver;
  DriverAction da;
  DriverInfo(Driver driver, DriverAction action) {
    this.driver = driver;
    da = action;
  }
  // ……
}

引申:

DriverInfo還包裝了DriverAction,DriverAction會(huì)在Driver被取消注冊(cè)時(shí)被調(diào)用,DriverAction的源碼注釋如下:

The JDBC driver's static initialization block must call DriverManager.registerDriver(Driver, DriverAction) in order to inform DriverManager which DriverAction implementation to call when the JDBC driver is de-registered.

MySQL的Driver在向DriverManager進(jìn)行注冊(cè)時(shí),DriverAction被設(shè)置為null

DriverManager類(lèi)

由上面的分析可得,Class.forName()方法調(diào)用后,com.mysql.cj.jdbc.Driver類(lèi)被加載,并執(zhí)行static { } 靜態(tài)代碼段,將com.mysql.cj.jdbc.Driver類(lèi)實(shí)例注冊(cè)到DriverManager中。然后,客戶(hù)端會(huì)調(diào)用DriverManager.getConnection()方法獲取一個(gè)Connection數(shù)據(jù)庫(kù)連接實(shí)例,該方法的部分源碼如下:

private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
  // ……
  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());
    }
  }
  // ……
}

DriverManager.getConnection()方法會(huì)遍歷registeredDrivers靜態(tài)字段,獲取字段內(nèi)保存的每一個(gè)Driver來(lái)嘗試響應(yīng)客戶(hù)端的數(shù)據(jù)庫(kù)連接請(qǐng)求,若所有Driver都連接數(shù)據(jù)庫(kù)失敗,則提示連接失敗信息

Connection接口

Connection代表和特定數(shù)據(jù)庫(kù)的連接會(huì)話(huà),能夠執(zhí)行SQL語(yǔ)句并在連接的上下文中返回執(zhí)行結(jié)果。

A connection (session) with a specific database. SQL statements are executed and results are returned within the context of a connection.

因此,DriverManager.getConnection()方法返回的Connection數(shù)據(jù)庫(kù)連接實(shí)例根據(jù)不同的數(shù)據(jù)庫(kù)有不同的實(shí)現(xiàn),MySQL的Connection接口實(shí)現(xiàn)關(guān)系如下:

源碼類(lèi)圖

根據(jù)源碼的分析,繪制類(lèi)圖如下:

對(duì)Driver和Connection進(jìn)行抽象,繪制類(lèi)圖如下:

模式體現(xiàn)

橋接模式通過(guò)組合/聚合關(guān)系代替繼承關(guān)系,實(shí)現(xiàn)抽象化和實(shí)現(xiàn)化部分的解耦。以上述JDBC在MySQL中的簡(jiǎn)略類(lèi)圖為例,抽象化部分有Driver接口和Connection接口,實(shí)現(xiàn)化部分有DriverManager。對(duì)于不同的數(shù)據(jù)庫(kù),Driver接口和Connection接口都有自己獨(dú)特的實(shí)現(xiàn)類(lèi)

但是,和Driver接口不同的是,Connection接口與DriverManager類(lèi)的關(guān)系只是聯(lián)系較弱的依賴(lài)關(guān)系,并不符合橋接模式的定義和特點(diǎn)。因此,在考慮橋接模式的情況下,可以再次將類(lèi)圖進(jìn)行簡(jiǎn)化:

最后,我們將其它數(shù)據(jù)庫(kù)的Driver接口實(shí)現(xiàn)也考慮在內(nèi),繪制類(lèi)圖如下:

橋接模式中的實(shí)現(xiàn)化(Implementor)角色對(duì)應(yīng)上圖的Driver接口,具體實(shí)現(xiàn)化(Concrete Implementor)角色對(duì)應(yīng)MysqlDriver、OracleDriver和MariadbDriver,擴(kuò)展抽象化 (Refined Abstraction)角色對(duì)應(yīng)DriverManager,不具有抽象化(Abstraction)角色作為擴(kuò)展抽象化角色的父類(lèi)

橋接模式的主要應(yīng)用場(chǎng)景是某個(gè)類(lèi)存在兩個(gè)獨(dú)立變化的維度,且這兩個(gè)維度都需要進(jìn)行擴(kuò)展,而現(xiàn)在僅有Driver一個(gè)變化維度,DriverManager沒(méi)有抽象化父類(lèi),它本身也沒(méi)有任何子類(lèi),因此我認(rèn)為,在JDBC中,是一種簡(jiǎn)化的橋接模式 —— 觀點(diǎn)一。

倘若JDBC針對(duì)Connection接口的設(shè)計(jì)不是將它作為Driver和DriverManager的"依賴(lài)"來(lái)處理,而是也作為一個(gè)變化的維度加入到橋接模式,或許能夠更好地體現(xiàn)JDBC對(duì)橋接模式的實(shí)現(xiàn),一種"假想"的橋接模式如下:

其它觀點(diǎn)二:JDBC采用的是策略模式而不是橋接模式

問(wèn)題源自知乎,但是沒(méi)有任何人做出解答,因?yàn)檫@確實(shí)和策略模式十分相似,如果把橋接模式的抽象部分簡(jiǎn)化來(lái)看,不去設(shè)計(jì)Abstraction,也就是用Refined Abstraction代替Abstraction,那么就類(lèi)似于策略模式的Context來(lái)使用接口的對(duì)象

但是,橋接模式和策略模式的目的是不一樣的,策略模式屬于對(duì)象行為模式(描述對(duì)象之間怎樣相互協(xié)作共同完成單個(gè)對(duì)象都無(wú)法單獨(dú)完成的任務(wù),以及怎樣分配職責(zé)),它的目的是封裝一系列的算法,使得算法可以相互替代,并在程序運(yùn)行的不同時(shí)刻選擇合適的算法。而橋接模式屬于對(duì)象結(jié)構(gòu)模式(描述如何將對(duì)象按某種布局組成更大的結(jié)構(gòu)),它的目的是將抽象與實(shí)現(xiàn)分離,使它們可以獨(dú)立變化

因此,從設(shè)計(jì)的目的來(lái)看,JDBC采用的并不是策略模式,在一段程序中數(shù)據(jù)庫(kù)驅(qū)動(dòng)并不存在頻繁地相互替換

其它觀點(diǎn)三:變化的維度一個(gè)是平臺(tái),另一個(gè)是數(shù)據(jù)庫(kù)

這是我認(rèn)同的一個(gè)觀點(diǎn),引用原文的話(huà)

變的是平臺(tái)和數(shù)據(jù)庫(kù),平臺(tái)在jvm這個(gè)層面就解決了,因?yàn)樗胁僮飨到y(tǒng)java基本都會(huì)提供對(duì)應(yīng)JDK,這也是"Once Write,Run AnyWhere"的原因。而數(shù)據(jù)庫(kù)則是依托公司的具體實(shí)現(xiàn),各個(gè)公司都提供對(duì)應(yīng)的Driver類(lèi),我用DriverManager類(lèi)進(jìn)行懶加載

考慮數(shù)據(jù)庫(kù)的實(shí)際應(yīng)用場(chǎng)景,我們可能在不同的操作系統(tǒng)上使用不同的數(shù)據(jù)庫(kù),但是JVM的平臺(tái)無(wú)關(guān)性使得我們不再有操作系統(tǒng)層面上的變化。假設(shè)不存在JVM,那么不同的客戶(hù)端加載和運(yùn)行數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序的代碼自然也各有不同,即DriverManager會(huì)因操作系統(tǒng)的變化而變化,不同的操作系統(tǒng)可以有不同的注冊(cè)Driver的方式

不過(guò)因?yàn)榇嬖贘VM,我們現(xiàn)在不再有"平臺(tái)"這一變化維度了

其它觀點(diǎn)四:變化的維度一個(gè)是客戶(hù)端應(yīng)用系統(tǒng),另一個(gè)是數(shù)據(jù)庫(kù)

一個(gè)比較獨(dú)特的觀點(diǎn),引用原文的話(huà)

應(yīng)用系統(tǒng)作為一個(gè)等級(jí)結(jié)構(gòu),與JDBC驅(qū)動(dòng)器這個(gè)等級(jí)結(jié)構(gòu)是相對(duì)獨(dú)立的,它們之間沒(méi)有靜態(tài)的強(qiáng)關(guān)聯(lián)。應(yīng)用系統(tǒng)通過(guò)委派與JDBC驅(qū)動(dòng)器相互作用,這是一個(gè)橋梁模式的例子。

原文筆者不認(rèn)為DriverManager作為Refined Abstraction角色存在,而是視作兩個(gè)變化維度之間的一個(gè)"過(guò)渡",原本的"橋"是Abstraction和Implementor之間的組合/聚合關(guān)系,而現(xiàn)在DriverManager類(lèi)本身成為了"橋",可以看作是橋梁模式的一個(gè)變體

新的觀點(diǎn)五:變化的維度一個(gè)是Driver,一個(gè)是Connection

如果從觀點(diǎn)四的原文筆者的角度看,把DriverManager類(lèi)本身作為"橋",那么我們還可以提出一種新的觀點(diǎn),繪制類(lèi)圖如下:

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • 詳解Java中native方法的使用

    詳解Java中native方法的使用

    native是與C++聯(lián)合開(kāi)發(fā)的時(shí)候用的!使用native關(guān)鍵字說(shuō)明這個(gè)方法是原生函數(shù),也就是這個(gè)方法是用C/C++語(yǔ)言實(shí)現(xiàn)的,并且被編譯成了DLL,由java去調(diào)用。本文給大家介紹java 中native方法使用,感興趣的朋友一起看看吧
    2020-09-09
  • Go&java算法之最大數(shù)示例詳解

    Go&java算法之最大數(shù)示例詳解

    這篇文章主要為大家介紹了Go&java算法之最大數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 解決mybatis竟然報(bào)Invalid value for getInt()的問(wèn)題

    解決mybatis竟然報(bào)Invalid value for getInt()的問(wèn)題

    使用mybatis遇到一個(gè)非常奇葩的問(wèn)題,總是報(bào)Invalid value for getInt()的問(wèn)題,怎么解決呢?下面小編通過(guò)場(chǎng)景分析給大家代來(lái)了mybatis報(bào)Invalid value for getInt()的解決方法,感興趣的朋友參考下吧
    2021-10-10
  • hadoop?詳解如何實(shí)現(xiàn)數(shù)據(jù)排序

    hadoop?詳解如何實(shí)現(xiàn)數(shù)據(jù)排序

    在很多業(yè)務(wù)場(chǎng)景下,需要對(duì)原始的數(shù)據(jù)讀取分析后,將輸出的結(jié)果按照指定的業(yè)務(wù)字段進(jìn)行排序輸出,方便上層應(yīng)用對(duì)結(jié)果數(shù)據(jù)進(jìn)行展示或使用,減少二次排序的成本
    2022-02-02
  • 詳解java調(diào)用存儲(chǔ)過(guò)程并封裝成map

    詳解java調(diào)用存儲(chǔ)過(guò)程并封裝成map

    這篇文章主要介紹了詳解java調(diào)用存儲(chǔ)過(guò)程并封裝成map的相關(guān)資料,希望通過(guò)本文能幫助到大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-09-09
  • java string類(lèi)方法深入解析

    java string類(lèi)方法深入解析

    以下是對(duì)java中的string類(lèi)方法進(jìn)行了詳細(xì)的分析介紹。需要的朋友可以過(guò)來(lái)參考下
    2013-08-08
  • Java實(shí)現(xiàn)json數(shù)據(jù)處理的常用腳本分享

    Java實(shí)現(xiàn)json數(shù)據(jù)處理的常用腳本分享

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)json數(shù)據(jù)處理的常用腳本,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以學(xué)習(xí)一下
    2023-03-03
  • Mybatis攔截器實(shí)現(xiàn)數(shù)據(jù)分表

    Mybatis攔截器實(shí)現(xiàn)數(shù)據(jù)分表

    當(dāng)數(shù)據(jù)量比較多時(shí),放在一個(gè)表中的時(shí)候會(huì)影響查詢(xún)效率,本文主要介紹了Mybatis攔截器實(shí)現(xiàn)數(shù)據(jù)分表,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式

    Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式

    在Java中緩存是一種常用的性能優(yōu)化技術(shù),用于在應(yīng)用程序中加速訪問(wèn)和查詢(xún)數(shù)據(jù)的速度,下面這篇文章主要給大家介紹了關(guān)于Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • Maven 主模塊和子模塊pom.xml依賴(lài)聲明

    Maven 主模塊和子模塊pom.xml依賴(lài)聲明

    這篇文章主要介紹了Maven 主模塊和子模塊pom.xml依賴(lài)聲明,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評(píng)論