Spring Boot 整合 Druid 指南【轉(zhuǎn)】

【轉(zhuǎn)自:https://developer.aliyun.com/article/1157595?accounttraceid=be1e563c5e6343acb1b5556c40306748kjzb

概述

簡介

Java 程序很大一部分要操作數(shù)據(jù)庫,為了提高性能操作數(shù)據(jù)庫的時候,又不得不使用數(shù)據(jù)庫連接池。

  • Druid 是阿里巴巴開源平臺上一個數(shù)據(jù)庫連接池實現(xiàn),結(jié)合了 C3P0、DBCP 等 DB 池的優(yōu)點,同時加入了日志監(jiān)控。
  • Druid 可以很好的監(jiān)控 DB 池連接和 SQL 的執(zhí)行情況,天生就是針對監(jiān)控而生的 DB 連接池。
  • Druid 已經(jīng)在阿里巴巴部署了超過600個應(yīng)用,經(jīng)過一年多生產(chǎn)環(huán)境大規(guī)模部署的嚴苛考驗。
  • Spring Boot 2.0 以上默認使用 Hikari 數(shù)據(jù)源,Hikari 與 Driud 都是當(dāng)前 Java Web 上最優(yōu)秀的數(shù)據(jù)源。
  • Druid 的 Github地址

Druid 基本配置參數(shù)介紹

com.alibaba.druid.pool.DruidDataSource 基本配置參數(shù)

  • name :數(shù)據(jù)源名稱

    如果存在多個數(shù)據(jù)源,監(jiān)控的時候可以通過名字來區(qū)分開來

    如果沒有配置,將會生成一個名字,格式是"DataSource-"+System.identityHashCode(this)

  • jdbcUrl :連接數(shù)據(jù)庫的 url,不同數(shù)據(jù)庫不一樣

  • username :連接數(shù)據(jù)庫的用戶名

  • password :連接數(shù)據(jù)庫的密碼

  • driverClassName :數(shù)據(jù)庫驅(qū)動類

    可配可不配,如果不配置 druid 會根據(jù) url 自動識別 dbType,然后選擇相應(yīng)的 driverClassName(建議配置下)

  • initialSize :初始化時建立物理連接的個數(shù),初始化發(fā)生在顯示調(diào)用 init 方法,或者第一次 getConnection 時

  • maxActive :最大連接池數(shù)量

  • maxIdle :已經(jīng)不再使用,配置了也沒效果

  • minIdle :最小連接池數(shù)量

  • maxWait :獲取連接時最大等待時間,單位毫秒

    配置了 maxWait 之后,缺省啟用公平鎖,并發(fā)效率會有所下降(可以通過配置 useUnfairLock=true 使用非公平鎖)

  • poolPreparedStatements :是否緩存 preparedStatement,即 PsCache

    PSCache 對支持游標的數(shù)據(jù)庫性能提升巨大,比如說 oracle,而 mysql 則建議關(guān)閉

  • maxOpenPreparedStatements :要啟用 PSCache,必須配置大于0

    當(dāng)大于 0 時,poolPreparedStatements 自動觸發(fā)修改為 true

    在 Druid 中,不會存在 Oracle 下 PSCache 占用內(nèi)存過多的問題,可以把這個數(shù)值配置大一點,比如 100

  • validationQuery :用來檢測連接是否有效的 sql,要求是一個查詢語句

    如果 validationQuery 為null,testOnBorrow、testOnReturn 、testWhileIdle 都不會起作用

  • testOnBorrow :申請連接時執(zhí)行 validationQuery 檢測連接是否有效,做了這個配置會降低性能

  • testOnReturn :歸還連接時執(zhí)行 validationQuery 檢測連接是否有效,做了這個配置會降低性能

  • testWhileIdle :建議配置為 true,不影響性能,并且保證安全性

    申請連接的時候檢測,如果空閑時間大于 timeBetweenEvictionRunMills,執(zhí)行 validationQuery 檢測連接是否有效

  • timeBetweenEvictionRunMillis :間隔多久才進行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒

    • Destory 線程會檢測連接的間隔時間
    • testWhileIdle 的判斷依據(jù)(詳見 testWhileIdele 屬性的說明)
  • numTestsPerEvictionRun :廢棄,一個 DruidDataSource 只支持一個 EvicationRun

  • minEvictableIdleTimeMillis :一個連接在池中最小生存的時間,單位是毫秒

  • connectionInitSqls :物理連接初始化的時候執(zhí)行 sql

  • exceptionSorter :當(dāng)數(shù)據(jù)庫拋出一些不可恢復(fù)的異常時,拋棄連接

  • filters :通過別名的方式配置擴展插件,屬性類型是字符串

    常用的插件有:監(jiān)控統(tǒng)計用的 filter(stat:監(jiān)控統(tǒng)計,log:4:日志記錄,wall:防御sql注入)

  • proxyFilters :類型是 List<com.alibaba.druid,filter.Filter>,如果同時配置 filter 和 proxyFilters,是組合關(guān)系(并非)

Druid 集成方式(含依賴)

Spring Boot 方式(推薦)

依賴

        <!--引入druid數(shù)據(jù)源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.6</version>
        </dependency>

yml 配置文件切換數(shù)據(jù)源

  • 方式1:

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/springboot_test?characterEncoding=utf8&serverTimezone=UTC
        username: root
        password: root
        # druid-spring-boot-starter 依賴自動生效 druid,可以不配置 type 屬性,但建議配置
        type: com.alibaba.druid.pool.DruidDataSource
    
  • 方式2:druid 專用配置(需要 druid-spring-boot-starter 依賴)

    spring:
      datasource:
        druid:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/springboot_test?characterEncoding=utf8&serverTimezone=UTC
          username: root
          password: root
    

Spring 方式

依賴

        <!--引入druid數(shù)據(jù)源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>

yml 配置文件切換數(shù)據(jù)源

Spring Boot 2.0 以上默認使用 com.zaxxer.hikari.HikariDataSource 數(shù)據(jù)源,但可以通過 spring.datasource.type 指定數(shù)據(jù)源。

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://192.168.10.132:3306/testdb
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    # druid 數(shù)據(jù)源專有配置
      # 不是druid-spring-boot-starter依賴,SpringBoot默認是不注入druid數(shù)據(jù)源專有屬性值的,需要自己綁定
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    # 配置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計。stat:監(jiān)控統(tǒng)計 log4:日志記錄 wall:防御sql注入
      # 如果運行時報錯:ClassNotFoundException:orgapache.log4j.Priority,則導(dǎo)入log4j依賴即可
    filters: stat,wall,log4j
    # 自動往數(shù)據(jù)庫建表
    #schema:
      #- classpath:department.sql

配置類:添加 DruidDataSource 組件到容器中,并綁定屬性

@Configuration
public class DruidDataSourceConfig {

    /**
     * 添加 DruidDataSource 組件到容器中,并綁定屬性
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource")
    public DataSource druid(){
        return  new DruidDataSource();
    }
}

Druid 密碼回調(diào)

現(xiàn)在很多項目都是把數(shù)據(jù)庫的密碼明文放在配置文件中,這樣其實是不安全的,應(yīng)該將密碼加密后再放到配置中,這樣可以一定程度的保護數(shù)據(jù)庫密碼的安全。

Druid 可以通過配置參數(shù) passwordCallBack 來指定一個密碼接口回調(diào)類進行密文密碼解密操作。

實現(xiàn)步驟:

  1. 自定義一個密碼接口回調(diào)類

    需要實現(xiàn) DruidPasswordCallback 接口并重寫接口方法 setProperties()

    import com.alibaba.druid.util.DruidPasswordCallback;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import java.util.Properties;
    
    @Slf4j
    public class DruidCustomPasswordCallback extends DruidPasswordCallback {
    
        @Override
        public void setProperties(Properties properties) {
            super.setProperties(properties);
            // 獲取配置文件中的已經(jīng)加密的密碼(spring.datasource.druid.connect-properties.password)
            String pwd = (String)properties.get("password");
            if (StringUtils.isNotEmpty(pwd)) {
                try {
                    // 這里的代碼是將密碼進行解密,并設(shè)置
                    String password = "解密后的明文密碼";
                    setPassword(password.toCharArray());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  2. yml 配置文件中配置密碼接口回調(diào)類

    注:

    • 可以通過配置文件中的 password-callback-class-name 或 password-callback 屬性來配置密碼接口回調(diào)類

      推薦使用 spring.datasource.druid.password-callback-class-name

      若是使用 spring.datasource.druid.password-callback 屬性,則需要搭配相應(yīng)的類型轉(zhuǎn)換器才能用,不然報錯:

      No converter found capable of converting from type [java.lang.String] to type [javax.security.auth.callback.PasswordCallback]
      
    • 根據(jù)密碼接口回調(diào)類中的密文密碼解密的邏輯,必須配置 spring.datasource.druid.connect-properties.password 屬性才會進行密文密碼解密操作并重置密碼為解密后的明文密碼

      若不配置 connect-properties.password ,則默認使用的 spring.datasource(.druid).password 的屬性值

    • 若是 Spring 集成 Druid,則可以在初始化 DruidDatabaseSource 時,手動配置其 passwordCallBack 屬性值

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
        username: root
        password: root11
    #    type: com.alibaba.druid.pool.DruidDataSource
        druid:
          # 配置密碼接口回調(diào)類的全限定類名
          password-callback-class-name: com.duran.ssmtest.config.DruidCustomPasswordCallback
          # 配置密碼接口回調(diào)類中方法入?yún)roperties的值(自定義Map<String, String>)
          connect-properties:
            # 配置密文密碼(傳參到配置密碼接口回調(diào)類中解密)
            password: aaa
    

Druid 數(shù)據(jù)源監(jiān)控

Druid 數(shù)據(jù)源具有監(jiān)控的功能,并提供了一個 web 界面方便用戶查看。

  • 設(shè)置 Druid 的后臺管理頁面,比如 登錄賬號、密碼 等;配置后臺管理

    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import javax.sql.DataSource;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class DruidDataSourceConfig {
    
        /**
         * 添加 DruidDataSource 組件到容器中,并綁定屬性
         */
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource")
        @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource")
        public DataSource druid(){
            return new DruidDataSource();
        }
    
        /**
         * 配置 Druid 監(jiān)控管理后臺的Servlet;
         * 內(nèi)置 Servlet 容器時沒有web.xml文件,所以使用 Spring Boot 的注冊 Servlet 方式
         */
        @Bean
        @ConditionalOnClass(DruidDataSource.class)
        public ServletRegistrationBean statViewServlet(){
            // 這些參數(shù)可以在 http.StatViewServlet 的父類 ResourceServlet 中找到
            Map<String,String> initParams = new HashMap<>();
            initParams.put("loginUsername","admin");
            initParams.put("loginPassword","123456");
            // allow:Druid 后臺允許誰可以訪問。默認就是允許所有訪問。
            initParams.put("allow",""); // 后面參數(shù)為空則所有人都能訪問,一般會寫一個具體的ip或ip段
            // deny:Druid 后臺禁止誰能訪問
            // initParams.put("deny","192.168.10.132");
    
            // 注冊一個servlet,同時表明/druid/* 這個請求會走到這個servlet,而druid內(nèi)置了這個請求的接收
            ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
            bean.setInitParameters(initParams);
            return bean;
        }
    
        /**
         * 配置一個web監(jiān)控的filter
         */
        @Bean
        @ConditionalOnClass(DruidDataSource.class)
        public FilterRegistrationBean webStatFilter(){
            Map<String,String> initParams = new HashMap<>();
            // 這些不進行統(tǒng)計
            initParams.put("exclusions","*.js,*.css,/druid/*");
    
            FilterRegistrationBean bean = new FilterRegistrationBean();
            bean.setFilter(new WebStatFilter());
            bean.setInitParameters(initParams);
            bean.setUrlPatterns(Arrays.asList("/*"));
            return  bean;
        }
    }
    
  • 運行應(yīng)用,瀏覽器訪問 :http://localhost:8080/druid/login.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容