Druid簡(jiǎn)介(Spring Boot + Mybatis + Druid數(shù)據(jù)源【自己定制】)

Druid的簡(jiǎn)介

Druid是一個(gè)非常優(yōu)秀的數(shù)據(jù)庫(kù)連接池。在功能、性能、擴(kuò)展性方面,都超過其他數(shù)據(jù)庫(kù)連接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。

Druid已經(jīng)在阿里巴巴部署了超過600個(gè)應(yīng)用,經(jīng)過一年多生產(chǎn)環(huán)境大規(guī)模部署的嚴(yán)苛考驗(yàn)。

Druid是一個(gè)JDBC組件,它包括三個(gè)部分:

  • 基于Filter-Chain模式的插件體系。

  • DruidDataSource 高效可管理的數(shù)據(jù)庫(kù)連接池。

  • SQLParser

Druid的功能

兼容DBCP

Druid提供了一個(gè)高效、功能強(qiáng)大、可擴(kuò)展性好的數(shù)據(jù)庫(kù)連接池。從DBCP遷移到Druid,只需要修改數(shù)據(jù)源的實(shí)現(xiàn)類就可以了。

強(qiáng)大的監(jiān)控特性

Druid內(nèi)置了一個(gè)功能強(qiáng)大的StatFilter插件可以監(jiān)控?cái)?shù)據(jù)庫(kù)訪問性能,可以清楚知道連接池和SQL的工作情況。

  • 監(jiān)控SQL的執(zhí)行時(shí)間、ResultSet持有時(shí)間、返回行數(shù)、更新行數(shù)、錯(cuò)誤次數(shù)、錯(cuò)誤堆棧信息。

  • SQL執(zhí)行的耗時(shí)區(qū)間分布。什么是耗時(shí)區(qū)間分布呢?比如說,某個(gè)SQL執(zhí)行了1000次,其中0-1毫秒?yún)^(qū)間50次,1-10毫秒800次,10-100毫秒100次,100-1000毫秒30次,1-10秒15次,10秒以上5次。通過耗時(shí)區(qū)間分布,能夠非常清楚知道SQL的執(zhí)行耗時(shí)情況。

  • 監(jiān)控連接池的物理連接創(chuàng)建和銷毀次數(shù)、邏輯連接的申請(qǐng)和關(guān)閉次數(shù)、非空等待次數(shù)、PSCache命中率等。

數(shù)據(jù)庫(kù)密碼加密

直接把數(shù)據(jù)庫(kù)密碼寫在配置文件中,這是不好的行為,容易導(dǎo)致安全問題。DruidDruiver和DruidDataSource都支持PasswordCallback。

SQL執(zhí)行日志

Druid提供了不同的LogFilter,能夠支持Common-Logging、Log4j和JdkLog,你可以按需要選擇相應(yīng)的LogFilter,監(jiān)控你應(yīng)用的數(shù)據(jù)庫(kù)訪問情況。

擴(kuò)展JDBC

如果你要對(duì)JDBC層有編程的需求,可以通過Druid提供的Filter機(jī)制,很方便編寫JDBC層的擴(kuò)展插件。

Druid在DruidDataSourc和ProxyDriver上提供了Filter-Chain模式的擴(kuò)展API,類似Serlvet的Filter,配置Filter攔截JDBC的方法調(diào)用。

SQLParser

SQL Parser是Druid的一個(gè)重要組成部分,它提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支持,這是一個(gè)手寫的高性能SQL Parser,支持Visitor模式,使得分析SQL的抽象語(yǔ)法樹很方便。

簡(jiǎn)單SQL語(yǔ)句用時(shí)10微秒以內(nèi),復(fù)雜SQL用時(shí)30微秒。

通過Druid提供的SQL Parser可以在JDBC層攔截SQL做相應(yīng)處理,比如防御SQL注入(WallFilter)、合并統(tǒng)計(jì)沒有參數(shù)化的SQL(StatFilter的mergeSql)、SQL格式化、分庫(kù)分表。

http://blog.csdn.net/qq_30444003/article/details/50795116

<style>table th:first-of-type { width: 100px;}</style>

Druid的配置詳解

配置 缺省值 說明
name 配置這個(gè)屬性的意義在于,如果存在多個(gè)數(shù)據(jù)源,監(jiān)控的時(shí)候可以通過名字來區(qū)分開來。如果沒有配置,將會(huì)生成一個(gè)名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此屬性至少在1.0.5版本中是不起作用的,強(qiáng)行設(shè)置name會(huì)出錯(cuò)。詳情-點(diǎn)此處
url 連接數(shù)據(jù)庫(kù)的url,不同數(shù)據(jù)庫(kù)不一樣。例如:<br />mysql : jdbc:mysql://10.20.153.104:3306/druid2<br /> oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 連接數(shù)據(jù)庫(kù)的用戶名
password 連接數(shù)據(jù)庫(kù)的密碼。如果你不希望密碼直接寫在配置文件中,可以使用ConfigFilter。詳細(xì)看這里
driverClassName 根據(jù)url自動(dòng)識(shí)別 這一項(xiàng)可配可不配,如果不配置druid會(huì)根據(jù)url自動(dòng)識(shí)別dbType,然后選擇相應(yīng)的driverClassName
initialSize 0 初始化時(shí)建立物理連接的個(gè)數(shù)。初始化發(fā)生在顯示調(diào)用init方法,或者第一次getConnection時(shí)
maxActive 8 最大連接池?cái)?shù)量
maxIdle 8 已經(jīng)不再使用,配置了也沒效果
minIdle 最小連接池?cái)?shù)量
maxWait 獲取連接時(shí)最大等待時(shí)間,單位毫秒。配置了maxWait之后,缺省啟用公平鎖,并發(fā)效率會(huì)有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
poolPreparedStatements false 是否緩存preparedStatement,也就是PSCache。PSCache對(duì)支持游標(biāo)的數(shù)據(jù)庫(kù)性能提升巨大,比如說oracle。在mysql下建議關(guān)閉。
maxPoolPreparedStatementPerConnectionSize -1 要啟用PSCache,必須配置大于0,當(dāng)大于0時(shí),poolPreparedStatements自動(dòng)觸發(fā)修改為true。在Druid中,不會(huì)存在Oracle下PSCache占用內(nèi)存過多的問題,可以把這個(gè)數(shù)值配置大一些,比如說100
validationQuery 用來檢測(cè)連接是否有效的sql,要求是一個(gè)查詢語(yǔ)句,常用select 'x'。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會(huì)起作用。
validationQueryTimeout 單位:秒,檢測(cè)連接是否有效的超時(shí)時(shí)間。底層調(diào)用jdbc Statement對(duì)象的void setQueryTimeout(int seconds)方法
testOnBorrow true 申請(qǐng)連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效,做了這個(gè)配置會(huì)降低性能。
testOnReturn false 歸還連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效,做了這個(gè)配置會(huì)降低性能。
testWhileIdle false 建議配置為true,不影響性能,并且保證安全性。申請(qǐng)連接的時(shí)候檢測(cè),如果空閑時(shí)間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測(cè)連接是否有效。
timeBetweenEvictionRunsMillis 1分鐘(1.0.14) 有兩個(gè)含義:<br />1) Destroy線程會(huì)檢測(cè)連接的間隔時(shí)間,如果連接空閑時(shí)間大于等于minEvictableIdleTimeMillis則關(guān)閉物理連接。<br />2) testWhileIdle的判斷依據(jù),詳細(xì)看testWhileIdle屬性的說明
numTestsPerEvictionRun 30分鐘(1.0.14) 不再使用,一個(gè)DruidDataSource只支持一個(gè)EvictionRun
minEvictableIdleTimeMillis 連接保持空閑而不被驅(qū)逐的最長(zhǎng)時(shí)間
connectionInitSqls 物理連接初始化的時(shí)候執(zhí)行的sql
exceptionSorter 根據(jù)dbType自動(dòng)識(shí)別 當(dāng)數(shù)據(jù)庫(kù)拋出一些不可恢復(fù)的異常時(shí),拋棄連接
filters 屬性類型是字符串,通過別名的方式配置擴(kuò)展插件,常用的插件有:<br />監(jiān)控統(tǒng)計(jì)用的filter:stat<br />日志用的filter:log4j<br />防御sql注入的filter:wall
proxyFilters 類型是List<com.alibaba.druid.filter.Filter>,如果同時(shí)配置了filters和proxyFilters,是組合關(guān)系,并非替換關(guān)系

Druid源碼

https://github.com/alibaba/druid/wiki

Druid常見問題

https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98

項(xiàng)目實(shí)踐

我們使用的是Spring boot + mybatis + Druid(自己定制)的架構(gòu)

導(dǎo)入依賴包

<!-- 數(shù)據(jù)庫(kù)驅(qū)動(dòng) -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>
<!--Mybatis 分頁(yè)插件 pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>
<!-- Druid連接池包 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.25</version>
</dependency>

配置數(shù)據(jù)源

目前Spring Boot中默認(rèn)支持的連接池有dbcp,dbcp2, tomcat, hikari四種連接池。

由于Druid暫時(shí)不在Spring Bootz中的直接支持,故需要進(jìn)行配置信息的定制:

新建配置類

package com.xiaolyuh.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "spring.datasource.druid")
public class DruidDataSourceProperty {
    // 數(shù)據(jù)庫(kù)配置
    private String url;

    private String username;

    private String password;

    private String driverClassName;

    // 初始化大小,最小,最大
    private int initialSize = 0;

    private int minIdle;

    private int maxActive = 8;

    // 配置獲取連接等待超時(shí)的時(shí)間
    private int maxWait;

    // 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒
    private int timeBetweenEvictionRunsMillis = 1000 * 60;

    // 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
    private int minEvictableIdleTimeMillis = 1000 * 60 * 30;

    // 檢測(cè)連接是否有效的sql
    private String validationQuery;

    private boolean testWhileIdle = false;

    private boolean testOnBorrow = true;

    private boolean testOnReturn = false;

    // PSCache Mysql下建議關(guān)閉
    private boolean poolPreparedStatements = false;

    private int maxPoolPreparedStatementPerConnectionSize = - 1;

    // 配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計(jì),'wall'用于防火墻
    private String filters;

    // 合并多個(gè)DruidDataSource的監(jiān)控?cái)?shù)據(jù)
    private boolean useGlobalDataSourceStat = false;

    private String connectionProperties;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public int getInitialSize() {
        return initialSize;
    }

    public void setInitialSize(int initialSize) {
        this.initialSize = initialSize;
    }

    public int getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public int getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
    }

    public int getMaxWait() {
        return maxWait;
    }

    public void setMaxWait(int maxWait) {
        this.maxWait = maxWait;
    }

    public int getTimeBetweenEvictionRunsMillis() {
        return timeBetweenEvictionRunsMillis;
    }

    public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    }

    public int getMinEvictableIdleTimeMillis() {
        return minEvictableIdleTimeMillis;
    }

    public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    }

    public String getValidationQuery() {
        return validationQuery;
    }

    public void setValidationQuery(String validationQuery) {
        this.validationQuery = validationQuery;
    }

    public boolean isTestWhileIdle() {
        return testWhileIdle;
    }

    public void setTestWhileIdle(boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
    }

    public boolean isTestOnBorrow() {
        return testOnBorrow;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public boolean isTestOnReturn() {
        return testOnReturn;
    }

    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public boolean isPoolPreparedStatements() {
        return poolPreparedStatements;
    }

    public void setPoolPreparedStatements(boolean poolPreparedStatements) {
        this.poolPreparedStatements = poolPreparedStatements;
    }

    public int getMaxPoolPreparedStatementPerConnectionSize() {
        return maxPoolPreparedStatementPerConnectionSize;
    }

    public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
        this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
    }

    public String getFilters() {
        return filters;
    }

    public void setFilters(String filters) {
        this.filters = filters;
    }


    public boolean isUseGlobalDataSourceStat() {
        return useGlobalDataSourceStat;
    }

    public void setUseGlobalDataSourceStat(boolean useGlobalDataSourceStat) {
        this.useGlobalDataSourceStat = useGlobalDataSourceStat;
    }

    public String getConnectionProperties() {
        return connectionProperties;
    }

    public void setConnectionProperties(String connectionProperties) {
        this.connectionProperties = connectionProperties;
    }
}

該配置類我們采用Spring boot推薦的安全配置方式,使用@ConfigurationProperties注解,聲明該類是一個(gè)配置類,并指定配置前綴。

配置連接池

package com.xiaolyuh.config;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

@Configuration
@ConditionalOnClass(com.alibaba.druid.pool.DruidDataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource", matchIfMissing = true)
@ServletComponentScan("com.xiaolyuh.filters")
public class DruidDataSourceConfig {

    private Logger logger = LoggerFactory.getLogger(DruidDataSourceConfig.class);

    @Autowired
    private DruidDataSourceProperty druidDataSourceProperty;


    @Bean     //聲明其為Bean實(shí)例
    @Primary  //在同樣的DataSource中,首先使用被標(biāo)注的DataSource
    public DataSource dataSource(){
        DruidDataSource datasource = new DruidDataSource();

        datasource.setUrl(druidDataSourceProperty.getUrl());
        datasource.setUsername(druidDataSourceProperty.getUsername());
        datasource.setPassword(druidDataSourceProperty.getPassword());
        datasource.setDriverClassName(druidDataSourceProperty.getDriverClassName());

        datasource.setInitialSize(druidDataSourceProperty.getInitialSize());
        datasource.setMinIdle(druidDataSourceProperty.getMinIdle());
        datasource.setMaxActive(druidDataSourceProperty.getMaxActive());
        // 配置獲取連接等待超時(shí)的時(shí)間
        datasource.setMaxWait(druidDataSourceProperty.getMaxWait());
        // 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒
        datasource.setTimeBetweenEvictionRunsMillis(druidDataSourceProperty.getTimeBetweenEvictionRunsMillis());
        // 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
        datasource.setMinEvictableIdleTimeMillis(druidDataSourceProperty.getMinEvictableIdleTimeMillis());
        datasource.setValidationQuery(druidDataSourceProperty.getValidationQuery());
        datasource.setTestWhileIdle(druidDataSourceProperty.isTestWhileIdle());
        datasource.setTestOnBorrow(druidDataSourceProperty.isTestOnBorrow());
        datasource.setTestOnReturn(druidDataSourceProperty.isTestOnReturn());
        datasource.setPoolPreparedStatements(druidDataSourceProperty.isPoolPreparedStatements());
        datasource.setMaxPoolPreparedStatementPerConnectionSize(druidDataSourceProperty.getMaxPoolPreparedStatementPerConnectionSize());
        datasource.setUseGlobalDataSourceStat(druidDataSourceProperty.isUseGlobalDataSourceStat());
        try {
            datasource.setFilters(druidDataSourceProperty.getFilters());
        } catch (SQLException e) {
            logger.error("druid configuration initialization filter", e);
        }
        datasource.setConnectionProperties(druidDataSourceProperty.getConnectionProperties());

        return datasource;
    }
    
//    // 配置Druid監(jiān)控的StatViewServlet和WebStatFilter
//    @Bean
//    public ServletRegistrationBean druidServlet() {
//      logger.info("init Druid Servlet Configuration ");
//      ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
//      servletRegistrationBean.setServlet(new StatViewServlet());
//      servletRegistrationBean.addUrlMappings("/druid/*");
//      Map<String, String> initParameters = new HashMap<String, String>();
//      initParameters.put("loginUsername", "admin");// 用戶名
//      initParameters.put("loginPassword", "admin");// 密碼
//      initParameters.put("resetEnable", "false");// 禁用HTML頁(yè)面上的“Reset All”功能
//      initParameters.put("allow", ""); // IP白名單 (沒有配置或者為空,則允許所有訪問)
//      //initParameters.put("deny", "192.168.20.38");// IP黑名單 (存在共同時(shí),deny優(yōu)先于allow)
//      servletRegistrationBean.setInitParameters(initParameters);
//      return servletRegistrationBean;
//    }
//
//    @Bean
//    public FilterRegistrationBean filterRegistrationBean() {
//      FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
//      filterRegistrationBean.setFilter(new WebStatFilter());
//      filterRegistrationBean.addUrlPatterns("/*");
//      filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
//      return filterRegistrationBean;
//    }
}  

該類被@Configuration標(biāo)注,聲明成配置類;DataSource對(duì)象被@Bean聲明,為Spring容器所管理, @Primary表示這里定義的DataSource將覆蓋其他來源的DataSource。

application.properties配置信息:

# 數(shù)據(jù)源配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.url=jdbc:mysql://localhost:3306/ssb_test
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.username=root
spring.datasource.druid.password=root
# 初始化大小,最小,最大
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-active=20
# 配置獲取連接等待超時(shí)的時(shí)間
spring.datasource.druid.max-wait=60000
# 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒
spring.datasource.druid.time-between-eviction-runs-millis=60000
# 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
spring.datasource.druid.min-evictable-idle-time-millis=300000
#檢測(cè)連接是否有效的sql
spring.datasource.druid.validation-query=SELECT 'x'
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
# PSCache Mysql下建議關(guān)閉
spring.datasource.druid.pool-prepared-statements=false
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=-1
# 配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計(jì),'wall'用于防火墻
spring.datasource.druid.filters=stat,wall,log4j
# 合并多個(gè)DruidDataSource的監(jiān)控?cái)?shù)據(jù)
spring.datasource.druid.use-global-data-source-stat=true
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

#mybatis
#entity掃描的包名
mybatis.type-aliases-package=com.xiaolyuh.domain.model
#Mapper.xml所在的位置
mybatis.mapper-locations=classpath*:/mybaits/*Mapper.xml
#開啟MyBatis的二級(jí)緩存
mybatis.configuration.cache-enabled=true

#pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql

到這里我們數(shù)據(jù)源就配置好了,寫一個(gè)測(cè)試類我們可以查看數(shù)據(jù)源是否生效。

測(cè)試類

package com.xiaolyuh;

import com.alibaba.fastjson.JSON;
import com.xiaolyuh.config.DruidDataSourceProperty;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

import javax.sql.DataSource;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DataSourceTests {

    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    DruidDataSourceProperty druidDataSourceProperty;

    @Test
    public void testDataSource() throws Exception {
        // 獲取配置的數(shù)據(jù)源
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        // 查看配置數(shù)據(jù)源信息
        System.out.println(dataSource.getClass().getName());
        System.out.println(JSON.toJSONString(druidDataSourceProperty));
    }

}

打印日志:

com.alibaba.druid.pool.DruidDataSource
{"connectionProperties":"druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000","driverClassName":"com.mysql.jdbc.Driver","filters":"stat,wall,log4j","initialSize":5,"maxActive":20,"maxPoolPreparedStatementPerConnectionSize":-1,"maxWait":60000,"minEvictableIdleTimeMillis":300000,"minIdle":5,"password":"root","poolPreparedStatements":false,"testOnBorrow":false,"testOnReturn":false,"testWhileIdle":true,"timeBetweenEvictionRunsMillis":60000,"url":"jdbc:mysql://localhost:3306/ssb_test","useGlobalDataSourceStat":true,"username":"root","validationQuery":"SELECT 'x'"}
2017-07-02 11:29:04.183  INFO 340 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@11fc564b: startup date [Sun Jul 02 11:28:58 CST 2017]; root of context hierarchy

Druid監(jiān)控的配置

下面我們來說一下Druid監(jiān)控的配置:

application.properties配置:

# 配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計(jì),'wall'用于防火墻
spring.datasource.druid.filters=stat,wall,log4j

使用這個(gè)filter的配置,開啟對(duì)監(jiān)控的支持。屬性類型是字符串,通過別名的方式配置擴(kuò)展插件,常用的插件有:

  • 監(jiān)控統(tǒng)計(jì)用的filter:stat
  • 日志用的filter:log4j
  • 防御sql注入的filter:wall

WebStatFilter和StatViewServlet配置

第一種方式使用@ServletComponentScan

這種配置監(jiān)控的方式是使用了原生的servlet,filter方式,然后通過@ServletComponentScan進(jìn)行啟動(dòng)掃描包的方式進(jìn)行處理的,你會(huì)發(fā)現(xiàn)我們的servlet,filter根本沒有任何的編碼。

WebStatFilter:

package com.xiaolyuh.filters;

import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;

import com.alibaba.druid.support.http.WebStatFilter;

//最后在App.Java類上加上注解:@ServletComponentScan是的spring能夠掃描到我們自己編寫的servlet和filter。
//
//注意不要忘記在 SpringBootSampleApplication.java 上添加 @ServletComponentScan 注解,不然就是404了。
//
//然后啟動(dòng)項(xiàng)目后訪問 http://127.0.0.1:8080/druid/index.html 即可查看數(shù)據(jù)源及SQL統(tǒng)計(jì)等。 
@WebFilter(filterName = "druidWebStatFilter", urlPatterns = "/*",
        initParams = {
                @WebInitParam(name = "exclusions", value = "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*")//忽略資源
        }
)
public class DruidStatFilter extends WebStatFilter {

} 

StatViewServlet:

package com.xiaolyuh.filters;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.alibaba.druid.support.http.StatViewServlet;

//最后在App.Java類上加上注解:@ServletComponentScan是的spring能夠掃描到我們自己編寫的servlet和filter。
//
//注意不要忘記在 SpringBootSampleApplication.java 上添加 @ServletComponentScan 注解,不然就是404了。
//
//然后啟動(dòng)項(xiàng)目后訪問 http://127.0.0.1:8080/druid/index.html 即可查看數(shù)據(jù)源及SQL統(tǒng)計(jì)等。 
@WebServlet(urlPatterns = "/druid/*",
        initParams = {
                @WebInitParam(name = "allow", value = "127.0.0.1,192.168.163.1"),// IP白名單(沒有配置或者為空,則允許所有訪問)
                @WebInitParam(name = "deny", value = "192.168.1.73"),// IP黑名單 (存在共同時(shí),deny優(yōu)先于allow)
                @WebInitParam(name = "loginUsername", value = "admin"),// 用戶名
                @WebInitParam(name = "loginPassword", value = "123456"),// 密碼
                @WebInitParam(name = "resetEnable", value = "false")// 禁用HTML頁(yè)面上的“Reset All”功能
        })
public class DruidStatViewServlet extends StatViewServlet {
    private static final long serialVersionUID = -2688872071445249539L;

}

最后在App.Java類上加上注解:@ServletComponentScan是的spring能夠掃描到我們自己編寫的servlet和filter。

注意:不要忘記在 SpringBootSampleApplication.java 上添加 @ServletComponentScan 注解,不然就是404了。

這個(gè)配置就類似于在web.xml配置filter和servlet一樣:

...
<servlet>    
    <servlet-name>DruidStatView</servlet-name>    
    <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>    
</servlet>    
<servlet-mapping>    
    <servlet-name>DruidStatView</servlet-name>    
    <url-pattern>/druid/*</url-pattern>    
</servlet-mapping>  
...
<filter>    
    <filter-name>DruidWebStatFilter</filter-name>    
    <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
    <init-param>    
        <param-name>exclusions</param-name>    
        <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>    
    </init-param>    
</filter>    
<filter-mapping>    
    <filter-name>DruidWebStatFilter</filter-name>    
    <url-pattern>/*</url-pattern>    
</filter-mapping>    
...

第二種方式,直接在配置類中注冊(cè)Bean

這種方式不使用使用@ServletComponentScan注解,在@Configuration配置類中使用代碼注冊(cè)Servlet。

// 配置Druid監(jiān)控的StatViewServlet和WebStatFilter
@Bean
public ServletRegistrationBean druidServlet() {
    logger.info("init Druid Servlet Configuration ");
  ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
  servletRegistrationBean.setServlet(new StatViewServlet());
  servletRegistrationBean.addUrlMappings("/druid/*");
  Map<String, String> initParameters = new HashMap<String, String>();
  initParameters.put("loginUsername", "admin");// 用戶名
  initParameters.put("loginPassword", "admin");// 密碼
  initParameters.put("resetEnable", "false");// 禁用HTML頁(yè)面上的“Reset All”功能
  initParameters.put("allow", ""); // IP白名單 (沒有配置或者為空,則允許所有訪問)
  //initParameters.put("deny", "192.168.20.38");// IP黑名單 (存在共同時(shí),deny優(yōu)先于allow)
  servletRegistrationBean.setInitParameters(initParameters);
  return servletRegistrationBean;
}

@Bean
public FilterRegistrationBean filterRegistrationBean() {
  FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
  filterRegistrationBean.setFilter(new WebStatFilter());
  filterRegistrationBean.addUrlPatterns("/*");
  filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
  return filterRegistrationBean;
}

打開監(jiān)控界面

瀏覽器中輸入:http://localhost/druid

輸入用戶名和密碼,上面配置的。

image

源碼

https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases

spring-boot-student-mybatis-druid 工程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評(píng)論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評(píng)論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評(píng)論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,779評(píng)論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評(píng)論 1 330
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評(píng)論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評(píng)論 0 291
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評(píng)論 1 338
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,515評(píng)論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,750評(píng)論 1 375
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,933評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評(píng)論 1 296
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,492評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,703評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容