最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用springboot通過spi機制加載mysql驅(qū)動的過程

 更新時間:2021年07月31日 14:55:43   作者:PolarisHuster  
這篇文章主要介紹了使用springboot通過spi機制加載mysql驅(qū)動的過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

SPI是一種JDK提供的加載插件的靈活機制,分離了接口與實現(xiàn),就拿常用的數(shù)據(jù)庫驅(qū)動來說,我們只需要在spring系統(tǒng)中引入對應的數(shù)據(jù)庫依賴包(比如mysql-connector-java以及針對oracle的ojdbc6驅(qū)動),然后在yml或者properties配置文件中對應的數(shù)據(jù)源配置就可自動使用對應的sql驅(qū)動,

比如mysql的配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/xxxxx?autoReconnect=true&useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: dev
    password: xxxxxx
    platform: mysql

spi機制正如jdk的classloader一樣,你不引用它,它是不會自動加載到jvm的,不是引入了下面的的兩個sql驅(qū)動依賴就必然會加載oracle以及mysql的驅(qū)動:

        <!--oracle驅(qū)動-->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>12.1.0.1-atlassian-hosted</version>
        </dependency>
 
        <!--mysql驅(qū)動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

正是由于jdk的這種spi機制,我們在spring項目中使用對應的驅(qū)動才這么簡單,

我們只需做兩件事:

1、在pom文件中引入對應的驅(qū)動依賴

2、在配置文件中配置對應的數(shù)據(jù)源即可

那么在spring項目中到底是誰觸發(fā)了數(shù)據(jù)庫驅(qū)動的spi加載機制呢?為了說明這個問題,咱們先說說jdk的spi的工作機制,jdk的spi通過ServiceLoader這個類來完成對應接口實現(xiàn)類的加載工作,就拿咱們要說的數(shù)據(jù)庫驅(qū)動來說,

ServiceLoader會在spring項目的classpath中尋找那些滿足下面條件的類:

1、這些jar包的META-INF/services有一個java.sql.Driver的文件

對應java.sql.Driver文件中為該數(shù)據(jù)庫驅(qū)動對應的數(shù)據(jù)庫驅(qū)動的實現(xiàn)類,比如mysql驅(qū)動對應的就是com.mysql.cj.jdbc.Driver,如下圖所示:

JDK這部分有關(guān)SPI具體的實現(xiàn)機制可以閱讀下ServiceLoader的內(nèi)部類LazyIterator,該類的hasNextService、nextService兩個方法就是具體SPI機制工作底層機制。

好了,上面簡要概述了下JDK的SPI工作機制,下面繼續(xù)看spring框架如何使用spi機制來完成數(shù)據(jù)庫驅(qū)動的自動管理的(加載、注銷),接下來就按照事情發(fā)展的先后的先后順序把mysql驅(qū)動加載的全過程屢一下,筆者使用的是springboot 2.x,數(shù)據(jù)源使用的數(shù)據(jù)源為Hikari,這是后來居上的一款數(shù)據(jù)源,憑借其優(yōu)秀的性能以及監(jiān)控機制成為了springboot 2.x之后首推的數(shù)據(jù)源,

用過springboot的小伙伴對springboot的自動裝載機制,數(shù)據(jù)源的配置也是使用的自動裝配機制,

具體類DataSourceAutoConfiguration

注意上面標紅部分,這里面引入的Hikari、Tomcat等(除了DataSourceJmxConfiguration之外)都是一些數(shù)據(jù)源配置,我們先看下

springboot推薦的Hikari數(shù)據(jù)源配置:

 
    /**
    ** 這是一個Configuration類,該類定義了創(chuàng)建HikariDataSource的Bean方法
   ***/
  	@Configuration
	@ConditionalOnClass(HikariDataSource.class)
	@ConditionalOnMissingBean(DataSource.class)
	@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
			matchIfMissing = true)
	static class Hikari {
 
		@Bean
		@ConfigurationProperties(prefix = "spring.datasource.hikari")
		public HikariDataSource dataSource(DataSourceProperties properties) {
            // 使用配置文件中的數(shù)據(jù)源配置來創(chuàng)建Hikari數(shù)據(jù)源
			HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
			if (StringUtils.hasText(properties.getName())) {
				dataSource.setPoolName(properties.getName());
			}
			return dataSource;
		}
 
	}

由于在DataSourceAutoConfiguration類中首先引入的就是Hikari的配置,DataSource沒有創(chuàng)建,滿足ConditionalOnMissingBean以及其他一些條件,就會使用該配置類創(chuàng)建數(shù)據(jù)源,好了接下來看下createDataSource到底是怎么創(chuàng)建數(shù)據(jù)源的,

這個過程又是怎么跟SPI關(guān)聯(lián)起來的

abstract class DataSourceConfiguration {
 
	@SuppressWarnings("unchecked")
	protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
        //使用DataSourceProperties數(shù)據(jù)源配置創(chuàng)建DataSourceBuilder對象(設計模式中的建造者模式)
		return (T) properties.initializeDataSourceBuilder().type(type).build();
	}
 
 
   //下面看下DataSourceBuilder的build方法
    public T build() {
        //在該例子中,type返回的是com.zaxxer.hikari.HikariDataSource類
		Class<? extends DataSource> type = getType();
        //實例化HikariDataSource類
		DataSource result = BeanUtils.instantiateClass(type);
		maybeGetDriverClassName();
        //bind方法中會調(diào)用屬性的設置,反射機制,在設置driverClassName屬性時
		bind(result);
		return (T) result;
	}
 
 
   // HikariConfig的方法,HikariDataSource繼承自HikariConfig類
public void setDriverClassName(String driverClassName)
   {
      checkIfSealed();
 
      Class<?> driverClass = null;
      ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader();
      try {
         if (threadContextClassLoader != null) {
            try {
                //加載driverClassName對應的類,即com.mysql.cj.jdbc.Driver類,該類為mysql對應的驅(qū)動類
               driverClass = threadContextClassLoader.loadClass(driverClassName);
               LOGGER.debug("Driver class {} found in Thread context class loader {}", driverClassName, threadContextClassLoader);
            }
            catch (ClassNotFoundException e) {
               LOGGER.debug("Driver class {} not found in Thread context class loader {}, trying classloader {}",
                            driverClassName, threadContextClassLoader, this.getClass().getClassLoader());
            }
         }
 
         if (driverClass == null) {
            driverClass = this.getClass().getClassLoader().loadClass(driverClassName);
            LOGGER.debug("Driver class {} found in the HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader());
         }
      } catch (ClassNotFoundException e) {
         LOGGER.error("Failed to load driver class {} from HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader());
      }
 
      if (driverClass == null) {
         throw new RuntimeException("Failed to load driver class " + driverClassName + " in either of HikariConfig class loader or Thread context classloader");
      }
 
      try {
         // 創(chuàng)建com.mysql.cj.jdbc.Driver對象,接下來看下com.mysql.cj.jdbc.Driver創(chuàng)建對象過程中發(fā)生了什么
         driverClass.newInstance();
         this.driverClassName = driverClassName;
      }
      catch (Exception e) {
         throw new RuntimeException("Failed to instantiate class " + driverClassName, e);
      }
   }
 
 
// com.mysql.cj.jdbc.Driver類
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            //調(diào)用DriverManager注冊自身,DriverManager使用CopyOnWriteArrayList來存儲已加載的數(shù)據(jù)庫驅(qū)動,然后當創(chuàng)建連接時最終會調(diào)用DriverManager的getConnection方法,這才是真正面向數(shù)據(jù)庫的,只不過spring的jdbc幫助我們屏蔽了這些細節(jié)
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

上面已經(jīng)來到了DriverManager類,那么DriverManager類里面是否有什么秘密呢,繼續(xù)往下走,

看下DriverManager的重要方法:

    static {
        //靜態(tài)方法,jvm第一次加載該類時會調(diào)用該代碼塊
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
 
    //DriverManager類的loadInitialDrivers方法
 
    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
 
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
             
                //這就是最終的謎底,最終通過ServiceLoader來加載SPI機制提供的驅(qū)動,本文用到了兩個,一個是mysql的,一個是oracle的,注意該方法只會在jvm第一次加載DriverManager類時才會調(diào)用,所以會一次性加載所有的數(shù)據(jù)庫驅(qū)動
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
 
                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
                 //下面的代碼就是真正完成數(shù)據(jù)庫驅(qū)動加載的地方,對應ServiceLoader類的LazyIterator類,所以看下該類的hasNext一級next方法即可,上面已經(jīng)講過,這里就不再贅述
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });
 
        println("DriverManager.initialize: jdbc.drivers = " + drivers);
 
        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

好了,上面已經(jīng)把springboot如何使用jdk的spi機制來加載數(shù)據(jù)庫驅(qū)動的,至于DriverManager的getConnection方法調(diào)用過程可以使用類似的方式分析下,在DriverManager的getConnection方法打個斷點,當代碼停在斷點處時,通過Idea或者eclipse的堆棧信息就可以看出個大概了。

但愿本文能幫助一些人了解mysql驅(qū)動加載的整個過程,加深對SPI機制的理解。希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot實現(xiàn)動態(tài)定時任務的示例代碼

    SpringBoot實現(xiàn)動態(tài)定時任務的示例代碼

    在SpringBoot項目中簡單使用定時任務,不過由于要借助cron表達式且都提前定義好放在配置文件里,不能在項目運行中動態(tài)修改任務執(zhí)行時間,實在不太靈活?,F(xiàn)在我們就來實現(xiàn)可以動態(tài)修改cron表達式的定時任務,感興趣的可以了解一下
    2022-10-10
  • Mybatis-Plus實現(xiàn)用戶ID自增出現(xiàn)的問題解決

    Mybatis-Plus實現(xiàn)用戶ID自增出現(xiàn)的問題解決

    項目基于 SpringBoot + MybatisPlus 3.5.2 使用數(shù)據(jù)庫自增ID時, 出現(xiàn)重復鍵的問題,本文就來介紹一下解決方法,感興趣的可以了解一下
    2023-09-09
  • SpringCloud中的Ribbon負載均衡詳細解讀

    SpringCloud中的Ribbon負載均衡詳細解讀

    這篇文章主要介紹了SpringCloud中的Ribbon負載均衡詳細解讀,當系統(tǒng)面臨大量的用戶訪問,負載過高的時候,通常會增加服務器數(shù)量來進行橫向擴展(集群),多個服務器的負載需要均衡,以免出現(xiàn)服務器負載不均衡,部分服務器負載較大,部分服務器負載較小的情況,需要的朋友可以參考下
    2023-11-11
  • IDEA插件(BindED)之查看class文件的十六進制

    IDEA插件(BindED)之查看class文件的十六進制

    這篇文章主要介紹了IDEA插件(BindED)之查看class文件的十六進制,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 詳解Spring中bean生命周期回調(diào)方法

    詳解Spring中bean生命周期回調(diào)方法

    本篇文章主要介紹了詳解Spring中bean生命周期回調(diào)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • JVM中最耗cpu的線程查找方法

    JVM中最耗cpu的線程查找方法

    這篇文章主要為大家介紹了JVM中最耗cpu的線程查找方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • Jar包反編譯修改代碼后重新打jar包圖文詳解

    Jar包反編譯修改代碼后重新打jar包圖文詳解

    這篇文章主要給大家介紹了關(guān)于Jar包反編譯修改代碼后重新打jar包的相關(guān)資料,JAR文件就是Java Archive(Java檔案文件),它是 Java的一種文檔格式,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • 解決IDEA鼠標點擊光標變大問題

    解決IDEA鼠標點擊光標變大問題

    這篇文章主要介紹了解決IDEA鼠標點擊光標變大問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • springboot+vue實現(xiàn)登錄功能的最新方法整理

    springboot+vue實現(xiàn)登錄功能的最新方法整理

    最近做項目時使用到了springboot+vue實現(xiàn)登錄功能的技術(shù),所以下面這篇文章主要給大家介紹了關(guān)于springboot+vue實現(xiàn)登錄功能的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • java對同一個文件進行讀寫操作方法

    java對同一個文件進行讀寫操作方法

    在本篇文章里我們給大家詳細講述了java對同一個文件進行讀寫操作的方法和知識點,需要的朋友們可以參考學習下。
    2018-10-10

最新評論

乳源| 东乡县| 鲁山县| 四平市| 英超| 临安市| 岫岩| 通辽市| 天镇县| 玉林市| 湘乡市| 怀来县| 棋牌| 全南县| 正镶白旗| 青冈县| 澎湖县| 宁海县| 肃宁县| 鄂托克旗| 昔阳县| 神池县| 忻城县| 台东县| 锦屏县| 霍山县| 岗巴县| 芦溪县| 子长县| 嘉定区| 平塘县| 镇宁| 毕节市| 西乌珠穆沁旗| 衡阳市| 台中县| 镇原县| 冷水江市| 冀州市| 中超| 赫章县|