前言

  • 对于数据访问层,无论是Sql还是NoSql,SpringBoot默认采用整合SpringData的方式进行统一管理,添加大量的自动配置,屏蔽了很多设置。引入了各种XxxTemplate和XxxRepository来简化我们队数据访问层的操作。

    • SpringBoot2.0默认是用com.zaxxer.hikari.HikariDataSource作为数据源。
    • 2.0以下默认采用的是org.apache.tomcat.jdbc.pool.DataSource作为数据源。
  • Hikari号称JAVA领域中最快的数据连接池,官方网站

  • 阿里巴巴奉献给apache的Druid服务,里面有监控中心,可以帮助我们快速定位慢sql等。

  • 为什么HikariCP被号称为性能最好的Java数据库连接池,如何配置使用参考这篇博客:点击

  • 注意事项:默认情况下使用com.zaxxer.hikari.HikariDataSource获取数据源时会抛出一个异常。说时区不对,需要我们修改mysql的时区:

    1
    2
    SHOW VARIABLES LIKE '%time_zone%'
    SET GLOBAL time_zone="+8:00"
  • 为什么选用Druid呢?

    • 性能够好,比c3p0,dbcp强一些
    • 经过考验,毕竟是阿里开源出来的项目
    • 最关键的是带一个建议的数据库监控
    • stat:Druid内置提供一个StatFilter,用于统计监控信息。
    • wall:Druid防御SQL注入攻击的WallFilter就是通过Druid的SQL Parser分析。Druid提供的SQL Parser可以在JDBC层拦截SQL做相应处理,比如说分库分表、审计等。
    • log4j2:这个就是 日志记录的功能,可以把sql语句打印到log4j2 供排查问题。

Druid配置文件

  • 配置Druid数据源(连接池): 如同以前 c3p0、dbcp 数据源可以设置数据源连接初始化大小、最大连接数、等待时间、最小连接数 等一样,Druid 数据源同理可以进行设置;
  • 配置 Druid web 监控 filter(WebStatFilter): 这个过滤器的作用就是统计 web 应用请求中所有的数据库信息,比如 发出的 sql 语句,sql 执行的时间、请求次数、请求的 url 地址、以及seesion 监控、数据库表的访问次数 等等。
  • 配置 Druid 后台管理 Servlet(StatViewServlet): Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装 路由器 时,人家也提供了一个默认的 web 页面;需要设置 Druid 的后台管理页面的属性,比如 登录账号、密码 等;
  • 具体的参数配置可以去官网看看:**Druid官方网站:http://druid.io/**、

YAML配置

  • 部分配置如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    server:
    port: 8888
    spring:
    datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    ######################## 数据源其他配置 ########################
    #初始化时建立物理连接的个数
    initial-size: 5
    #最小连接池数量
    min-idle: 5
    #最大连接池数量 maxIdle已经不再使用
    max-active: 20
    #获取连接时最大等待时间,单位毫秒
    max-wait: 60000
    #既作为检测的间隔时间又作为testWhileIdel执行的依据
    time-between-eviction-runs-millis: 60000
    #销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
    min-evictable-idle-time-millis: 30000
    #用来检测连接是否有效的sql 必须是一个查询语句
    #mysql中为 select 'x'
    #oracle中为 select 1 from dual
    validation-query: select 'x'
    #申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
    test-while-idle: true
    #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
    test-on-borrow: false
    #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
    test-on-return: false
    #是否缓存preparedStatement 也就是PSCache 官方建议MySQL下建议关闭 个人建议如果想用SQL防火墙 建议打开
    pool-prepared-statements: true
    #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
    max-pool-prepared-statement-per-connection-size: 20
    #连接池中的minIdle数量以内的连接,闲置时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作
    #keep-alive: true
    #Spring 监控,利用aop 对指定切面的执行时间,jdbc数进行记录
    aop-patterns: pers.fulsun.service.*,pers.fulsun.dao.*,pers.fulsun.controller.*,pers.fulsun.mapper.*
    ######################## 配置监控统计拦截 ########################
    #stat必须第一个: 监控统计、Log4j:日志记录、waLL: 防御sqL注入
    filters: stat,wall,slf4j
    filter:
    stat:
    enabled: true
    #开启慢sql监控,超过2s 就认为是慢sql,记录到日志中
    log-slow-sql: true
    slow-sql-millis: 2000
    config:
    #启用ConfigFilter
    #enabled: true
    ######################## druid连接池监控 ########################
    #### 配置StatViewServlet(监控页面),用于展示Druid的统计信息 ######
    stat-view-servlet:
    enabled: true
    #内置监控页面的首页是/druid/index.html
    url-pattern: /druid/*
    #不允许清空统计资料,重新计算
    reset-enable: false
    # 配置监控页面访问账号
    login-username: admin
    # 配置监控页面访问密码
    login-password: admin
    #黑名单: 优先于白名单
    deny:
    # 白名单:为空,则允许所有访问
    allow:
    #### 配置 WebStatFilter,用于采集web关联监控的数据息 ######
    web-stat-filter:
    enabled: true
    #过滤所有url
    url-pattern: /*
    #排除一些不必要的url
    exclusions: "*.gif,*.png,*.jpg,*.html,*.js,*.css,*.ico,/druid/*"
    #开启session统计功能
    session-stat-enable: true
    #session的最大个数,默认1000
    session-stat-max-count: 1000

porperties

  • properties方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    # 服务器HTTP端口,默认:8080
    server.port=8088
    # 应用程序的上下文路径
    server.servlet.context-path=/
    # 会话超时。 如果未指定持续时间后缀,则将使用秒。默认30m
    server.servlet.session.timeout=30m

    # 用于解码URI的字符编码,默认:UTF-8
    server.tomcat.uri-encoding=UTF-8
    # Tomcat基本目录。 如果未指定,则使用一个临时目录。
    server.tomcat.basedir=target/temp
    # 服务器在任何给定时间接受和处理的最大连接数。 一旦达到限制,操作系统仍然可以基于“ acceptCount”属性接受连接。默认 8192
    server.tomcat.max-connections=2000
    # 工作线程的最大数量。默认:200
    server.tomcat.threads.max=200
    # 连接器在接受连接后将等待呈现请求URI行的时间。
    server.tomcat.connection-timeout=8000

    #datasource
    # 连接资料库的url,不同资料库不一样。
    spring.datasource.druid.url=jdbc:postgresql://10.213.133.130:5432/zbdb
    # 连接资料库的用户名
    spring.datasource.druid.username=lgszz
    # 连接资料库的密码。
    spring.datasource.druid.password=lgszz.2020

    # druid 配置
    # 要使用的连接池实现的完全限定名称。 默认情况下,它是从类路径中自动检测到的。
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
    spring.datasource.druid.initial-size=10
    # 最大连接池数量
    spring.datasource.druid.max-active=50
    # 最小连接池数量
    spring.datasource.druid.min-idle=10
    # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,併发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
    spring.datasource.druid.max-wait=3000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    spring.datasource.druid.time-between-eviction-runs-millis=60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    spring.datasource.druid.min-evictable-idle-time-millis=300000
    # 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validation-query为null,test-on-borrow、test-on-return、test-while-idle都不会起作用。
    spring.datasource.druid.validation-query=SELECT 1
    # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果閒置时间大于time-between-eviction-runs-millis,执行validation-query检测连接是否有效。默认为false
    spring.datasource.druid.test-while-idle=true
    # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认为true
    spring.datasource.druid.test-on-borrow=true
    # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认为false
    spring.datasource.druid.test-on-return=false
    # 是否缓存preparedStatement,也就是PSCache。PSCache对支援游标的资料库性能提升巨大,比如说oracle。在mysql下建议关闭。
    spring.datasource.druid.pool-prepared-statements=true
    # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
    spring.datasource.druid.max-pool-prepared-statement-per-connection-size=50
    # 连接池中的minIdle数量以内的连接,閒置时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作
    #spring.datasource.druid.keep-alive=true
    # Spring 监控,利用aop 对指定介面的执行时间,jdbc数进行记录
    #spring.datasource.druid.aop-patterns="com.springboot.template.dao.*"

    ########### 启用内置筛检程式(第一个 stat必须,否则监控不到SQL)##########
    # 配置监控统计拦截的filters,去掉后监控介面SQL无法进行统计,属性类型是字串,通过别名的方式配置扩展外挂程式,常用的外挂程式有:监控统计用的filter:stat,日志用的filter:log4j,防御sql注入的filter:wall
    spring.datasource.druid.filters=config,stat,slf4j,wall
    # 开启druid datasource 的状态监控
    spring.datasource.druid.filter.stat.enabled=true
    # 开启慢sql监控,超过2s 就认为是慢sql,记录到日志中
    spring.datasource.druid.filter.stat.log-slow-sql=true
    spring.datasource.druid.filter.stat.slow-sql-millis=2000
    # 配置 connection-properties,启用加密
    #spring.datasource.druid.connection-properties=config.decrypt=true;
    # 启用ConfigFilter
    #spring.datasource.druid.filter.config.enabled=true

    ########## druid连接池监控 ##########
    ########## 配置StatViewServlet(监控页面),用于展示Druid的统计信息 ##########
    # 启用StatViewServlet
    spring.datasource.druid.stat-view-servlet.enabled=true
    # 访问内置监控页面的路径,内置监控页面的首页是/druid/index.html
    spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
    # 不允许清空统计资料,重新计算
    spring.datasource.druid.stat-view-servlet.reset-enable=false
    # 配置监控页面访问账号
    spring.datasource.druid.stat-view-servlet.login-username=admin
    # 配置监控页面访问密码
    spring.datasource.druid.stat-view-servlet.login-password=admin
    # 白名单,允许访问的位址,如果allow没有配置或者为空,则允许所有访问
    spring.datasource.druid.stat-view-servlet.allow=
    # 黑名单,拒绝访问的位址,deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝
    spring.datasource.druid.stat-view-servlet.deny=

    ########## 配置WebStatFilter,用于採集web关联监控的数据 ##########
    # 启用StatFilter
    spring.datasource.druid.web-stat-filter.enabled=true
    # 过滤所有url
    spring.datasource.druid.web-stat-filter.url-pattern=/*
    # 排除一些不必要的url
    spring.datasource.druid.web-stat-filter.exclusions=*.gif,*.png,*.jpg,*.html,*.js,*.css,*.ico,/druid/*
    # 开启session统计功能
    spring.datasource.druid.web-stat-filter.session-stat-enable=true
    # session的最大个数,默认1000
    spring.datasource.druid.web-stat-filter.session-stat-max-count=1000

连接池配置的说明

配置 缺省值 说明
name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:”DataSource-“ + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错。详情-点此处
url 连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 连接数据库的用户名
password 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里
driverClassName 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxPoolPreparedStatementPerConnectionSize -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句,常用select ‘x’。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
validationQueryTimeout 单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
keepAlive false (1.0.28) 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
timeBetweenEvictionRunsMillis 1分钟(1.0.14) 有两个含义: 1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun 30分钟(1.0.14) 不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis 连接保持空闲而不被驱逐的最小时间
connectionInitSqls 物理连接初始化的时候执行的sql
exceptionSorter 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall
proxyFilters 类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

SpringBoot整合Druid

引入依赖

  • 以往我们都是直接引入Druid的依赖:

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.12</version>
    </dependency>
  • apache中已经出了一套完美支持SpringBoot的方案所以说我们不使用上面的依赖而是使用:官方文档:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>pers.fulsun</groupId>
    <artifactId>druid-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- springBoot父依赖 -->
    <parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>2.1.9.RELEASE</version>
    </parent>

    <dependencies>
    <!-- yaml文件配置提示 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <!-- web场景启动器 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 测试场景启动器 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    <!-- 引入JDBC场景启动器 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!-- 数据库连接依赖 -->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- 引入 druid 场景启动器 -->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.21</version>
    </dependency>
    <!-- druid如果启用日志记录时,需要导入Log4j 依赖-->
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    </dependencies>
    </project>

编写配置文件

  • 配置文件如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    server:
    port: 8888
    spring:
    datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    ######################## 数据源其他配置 ########################
    #初始化时建立物理连接的个数
    initial-size: 5
    #最小连接池数量
    min-idle: 5
    #最大连接池数量 maxIdle已经不再使用
    max-active: 20
    #获取连接时最大等待时间,单位毫秒
    max-wait: 60000
    #既作为检测的间隔时间又作为testWhileIdel执行的依据
    time-between-eviction-runs-millis: 60000
    #销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
    min-evictable-idle-time-millis: 30000
    #用来检测连接是否有效的sql 必须是一个查询语句
    #mysql中为 select 'x'
    #oracle中为 select 1 from dual
    validation-query: select 'x'
    #申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
    test-while-idle: true
    #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
    test-on-borrow: false
    #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
    test-on-return: false
    #是否缓存preparedStatement 也就是PSCache 官方建议MySQL下建议关闭 个人建议如果想用SQL防火墙 建议打开
    pool-prepared-statements: true
    #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
    max-pool-prepared-statement-per-connection-size: 20
    #连接池中的minIdle数量以内的连接,闲置时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作
    #keep-alive: true
    #Spring 监控,利用aop 对指定切面的执行时间,jdbc数进行记录
    aop-patterns: pers.fulsun.service.*,pers.fulsun.dao.*,pers.fulsun.controller.*,pers.fulsun.mapper.*

启用数据源

  • 虽然我们配置了druid连接池的一些属性,但是默认是不会生效。因为默认是使用的java.sql.Datasource的类来获取属性的,有些属性datasource没有。

  • 如果我们想让配置生效,需要手动创建Druid的配置文件,创建Druiddatasource。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package top.fulsun.config;

    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    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.HashMap;
    import java.util.Map;

    /**
    * @Description: Druid的配置文件
    * @version: v1.0.0
    * @author: fulsun
    * @createDate 2020/4/29 19:27
    * @updateUser
    * @updateDate
    */
    @Configuration
    public class DruidConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid")
    public DataSource druidDataSource() {
    return new DruidDataSource();
    }
    }

测试数据源

  • 创建测试类检验数据源配置是否完成,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package pers.fulsun;

    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.test.context.junit4.SpringRunner;

    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class TestDatasource {
    @Autowired
    private DataSource dataSource;

    @Test
    public void contextLoad() throws SQLException {
    System.out.println(dataSource.getClass());
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
    }

    }

    // ----------- 结果如下------------
    class com.alibaba.druid.pool.DruidDataSource
    2020-04-29 15:39:50.142 INFO 16628 --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
    com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7e0674f5

监控功能

  • Druid的最强大之处在于它有着强大的监控,可以监控我们发送到数据库的所有sql语句。方便我们后期排插错误。

能监控哪些数据

  1. 数据源
  2. SQL监控 对执行的MySQL语句进行记录,并记录执行时间、事务次数等
  3. SQL防火墙 对SQL进行预编译,并统计该条SQL的数据指标
  4. Web应用对发布的服务进行监控,统计访问次数,并发数等全局信息
  5. URI监控对访问的URI进行统计,记录次数,并发数,执行jdbc数等
  6. Session监控 对用户请求后保存在服务器端的session进行记录,识别出每个用户访问了多少次数据库等
  7. Spring监控 (按需配置)利用aop对各个内容接口的执行时间、jdbc数进行记录

Druid的工程结构

  • 统计相关的内容都在stat包中,比较核心的就是DruidStatService,很多服务通过这个类暴露出来,例如统计数据获取等。

  • 元数据还可以通过DruidStatManagerFacade获取。

  • 统计页面主要是通过StatViewServlet来注册的,这个类的集成结构是 StatViewServlet -> ResourceServlet -> HttpServlet,就是这个Servlet来映射相关的统计界面的,关于监控页面的源html,可查看support包下的http->resourses

  • Filter插件,stat功能(监控)、wall功能(sql防火墙)、log4j2功能(监控日志输出),都是以插件的形式配置的

启用监控页面

  • 配置 Druid 后台管理 Servlet(StatViewServlet): Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装 路由器 时,人家也提供了一个默认的 web 页面;需要设置 Druid 的后台管理页面的属性,比如 登录账号、密码 等;

配置类方式

  • 我们接着在DruidConfig里面配置监控中心:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 配置监控服务器
    * @return 返回监控注册的servlet对象
    * @author fulsun
    */
    @Bean
    public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    //ServletRegistrationBean reg = new ServletRegistrationBean();
    //reg.setServlet(new StatViewServlet());
    //reg.addUrlMappings("/druid/*");
    // 添加IP白名单
    servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
    // 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
    //servletRegistrationBean.addInitParameter("deny", "127.0.0.1");
    // 添加控制台管理用户
    servletRegistrationBean.addInitParameter("loginUsername", "admin");
    servletRegistrationBean.addInitParameter("loginPassword", "123456");
    // 是否能够重置数据
    servletRegistrationBean.addInitParameter("resetEnable", "false");
    return servletRegistrationBean;
    }
  • 配置完后我们启动SpringBoot程序访问:http://localhost:8080/druid/ 就可以来到我们的登录页面面就是我们上面添加的控制台管理用户,我们可以在上面很好的看到运行状况.

配置文件方式

  • 注销上面的statViewServlet的@bean注解,或注释方法

  • Yaml文件下添加下面配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    spring:
    datasource:
    druid:
    stat-view-servlet:
    enabled: true
    #内置监控页面的首页是/druid/index.html
    url-pattern: /druid/*
    #不允许清空统计资料,重新计算
    reset-enable: false
    # 配置监控页面访问账号
    login-username: admin
    # 配置监控页面访问密码
    login-password: admin
    #黑名单: 优先于白名单
    deny:
    # 白名单:为空,则允许所有访问
    allow:
  • 启动SpringBoot程序访问:http://localhost:8080/druid/ ,效果同上

配置监控统计拦截

  • 虽然管理后台能够访问,但是监控导航下没有数据,需要启用监控拦截器

  • Yaml文件中添加下面配置

    • stat必须第一个: 监控统计、Log4j:日志记录、waLL: 防御sqL注入

      1
      2
      3
      4
      spring:
      datasource:
      druid:
      filters: stat,wall,log4j
  • 操作SQL

    • 创建数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      create database if not exists demo;
      use demo;
      DROP TABLE IF EXISTS `user`;
      CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) DEFAULT NULL,
      `age` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

      INSERT INTO `user` VALUES ('1', '张三', '23');

    • 编写基本类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      pulic class UserRowMapper implements RowMapper<User> {
      @Override
      public User mapRow(ResultSet rs, int rowNum) throws SQLException {
      User user = new User();
      user.setId(rs.getInt("id"));
      user.setName(rs.getString("name"));
      user.setAge(rs.getInt("age"));
      return user;
      }

      }

      // --------------------------
      import lombok.Data;

      @Data
      public class User {
      private Integer id;

      private String name;

      private Integer age;
      }
    • 编写controller层代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @RestController
      public class TestStatController {
      @Autowired
      private JdbcTemplate jdbcTemplate;

      @GetMapping("/user/all")
      public List<User> testStat() {
      List<User> query = jdbcTemplate.query("select * from user", new UserRowMapper());
      return query;
      }
      }
  • 访问http://localhost:8888/user/all后,可以看到数据源, sql监控, sql防火墙, spring监控有记录产生

  • 数据源:这里可以看到之前我们配置的数据库连接池信息以及当前使用情况的各种指标。

  • SQL监控:该数据源中执行的SQL语句极其统计数据。在这个页面上,我们可以很方便的看到当前这个Spring Boot都执行过哪些SQL,这些SQL的执行频率和执行效率也都可以清晰的看到。如果你这里没看到什么数据?用这些Controller接口触发对数据库的操作。

  • 这里我们可以通过调用接口的方式去触发一些操作,这样SQL监控页面就会产生一些数据:

  • 图中监控项上,执行时间、读取行数、更新行数都通过区间分布的方式表示,将耗时分布成8个区间:

    • 0 - 1 耗时0到1毫秒的次数
    • 1 - 10 耗时1到10毫秒的次数
    • 10 - 100 耗时10到100毫秒的次数
    • 100 - 1,000 耗时100到1000毫秒的次数
    • 1,000 - 10,000 耗时1到10秒的次数
    • 10,000 - 100,000 耗时10到100秒的次数
    • 100,000 - 1,000,000 耗时100到1000秒的次数
    • 1,000,000 - 耗时1000秒以上的次数
  • 记录耗时区间的发生次数,通过区分分布,可以很方便看出SQL运行的极好、普通和极差的分布。 耗时区分分布提供了“执行+RS时分布”,是将执行时间+ResultSet持有时间合并监控,这个能方便诊断返回行数过多的查询。

  • SQL防火墙:该页面记录了与SQL监控不同维度的监控数据,更多用于对表访问维度、SQL防御维度的统计。该功能数据记录的统计需要在spring.datasource.druid.filters中增加wall属性才会进行记录统计,这里的所有监控信息是对这个应用实例的数据源而言的,而并不是数据库全局层面的,可以视为应用层的监控,不可能作为中间件层的监控。

采集web关联监控的数据

  • 配置 Druid web 监控 filter(WebStatFilter): 这个过滤器的作用就是统计 web 应用请求中所有的数据库信息,比如 发出的 sql 语句,sql 执行的时间、请求次数、请求的 url 地址、以及seesion 监控、数据库表的访问次数 等等。

配置类

  • 我们接着在DruidConfig里面配置监控过滤器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 配置服务过滤器
    * @return 返回过滤器配置对象
    */
    @Bean
    public FilterRegistrationBean statFilter() {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
    // 添加过滤规则
    filterRegistrationBean.addUrlPatterns("/*");
    // 忽略过滤格式
    filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,");
    return filterRegistrationBean;
    }
  • 重启Springboot应用后, web应用, URL监控, Session监控 会产生数据.

配置文件

  • 注释配置文件的statFilter方法

  • Yaml文件添加下面配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    spring:
    datasource:
    druid:
    web-stat-filter:
    enabled: true
    #过滤所有url
    url-pattern: /*
    #排除一些不必要的url
    exclusions: "*.gif,*.png,*.jpg,*.html,*.js,*.css,*.ico,/druid/*"
    #开启session统计功能
    session-stat-enable: true
    #session的最大个数,默认1000
    session-stat-max-count: 1000
  • 重启访问controller接口,可以看到效果路上图

接口测试

  • 使用MockMVC对 controller 接口进行测试

  • junit中的注解,当一个类用@RunWith注释或继承一个用@RunWith注释的类时,JUnit将调用它所引用的类来运行该类中的测试而不是开发者去在junit内部去构建它。

    • JUnit5的环境下写@Before 和@After 被 @BeforeEach 和@AfterEach给替代
  • @SpringBootTest:作用是加载ApplicationContext,启动spring容器。使用@SpringBootTest时并没有像@ContextConfiguration一样显示指定locations或classes属性,原因在于@SpringBootTest注解会自动检索程序的配置文件,检索顺序是从当前包开始,逐级向上查找@SpringBootApplication @SpringBootConfiguration注解的类。

    • 常用的配置项如下:

      1
      2
      3
      value 指定配置属性
      properties 指定配置属性,和value意义相同
      classes 指定配置类,等同于@ContextConfiguration中的class,若没有显示指定,将查找嵌套的@Configuration类,然后返回到SpringBootConfiguration搜索配置
    • webEnvironment 指定web环境,可选值有:MOCK、RANDOM_PORT、DEFINED_PORT、NONE

      • MOCK 此值为默认值,该类型提供一个mock环境,此时内嵌的服务(servlet容器)并没有真正启动,也不会监听web端口。
      • RANDOM_PORT 启动一个真实的web服务,监听一个随机端口。
      • DEFINED_PORT 启动一个真实的web服务,监听一个定义好的端口(从配置中读取)。
      • NONE启动一个非web的ApplicationContext,既不提供mock环境,也不提供真是的web服务
  • 在集成Druid 开启监控功能后会启用拦截器,MOCK 环境默认不会初始化Filter,servlet,会出现空指针问题,有如下的解决方式

    • 使用RANDOM_PORT 或DEFINED_PORT 的环境,在初始化的时候添加拦截器
    • 在测试类上增加@activeprofiles(“test”),在test的配置文件中设置:spring.datasource.druid.web-stat-filter.enabled=false

测试API

  • 基本的测试代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    package tk.fulsun.demo.controller;

    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;

    /**
    * 不会对Filter、Servlet进行初始化 @SpringBootTest(webEnvironment = WebEnvironment.MOCK)
    *
    * @author fulsun
    * @description: 控制层接口测试
    * @date 5/27/2021 10:57 AM
    */
    //SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class
    @RunWith(SpringRunner.class)
    //SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    class TestControllerTest {

    private MockMvc mvc;

    /**
    * web项目上下文
    */
    @Autowired
    private WebApplicationContext webApplicationContext;

    /**
    * 获取拦截器列表
    */
    @Autowired
    private FilterRegistrationBean filterRegistrationBean;

    /**
    * 所有测试方法执行之前执行该方法
    */
    @BeforeEach
    public void before() {
    System.out.println("------start test -------");
    // 获取mockmvc对象实例
    this.mvc =
    MockMvcBuilders.webAppContextSetup(webApplicationContext)
    .addFilters(filterRegistrationBean.getFilter())
    .build();
    }

    @Test
    public void controllerTest() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/users"))
    .andExpect(MockMvcResultMatchers.status().isOk())
    .andDo(MockMvcResultHandlers.print());
    }
    }

使用注解方式创建MockMvc

  • 上面代码改写如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    @AutoConfigureMockMvc
    class TestControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void controllerTest() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/users").accept(MediaType.APPLICATION_JSON_VALUE))
    .andExpect(MockMvcResultMatchers.status().isOk())
    .andDo(MockMvcResultHandlers.print());
    }
    }