Spring Boot学习笔记(十一)Druid数据源及动态数据源配置

一、普通方式配置

即不使用springboot的自动配置方式配置Druid数据源。
pom文件:

<dependencies>
       
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-aopartifactId>
        dependency>

        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.2.2version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.12version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.44version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-configuration-processorartifactId>
            <optional>trueoptional>
        dependency>
    dependencies>

druid.properties文件:

# 数据源配置
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
# 使用阿里的Druid连接池
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
# 填写你数据库的url、登录名、密码和数据库名
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/test?useSSL=FALSE&useUnicode=TRUE&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.datasource.username = 账号
spring.datasource.password = 密码

# 连接池的配置信息
# 初始化大小,最小,最大
spring.datasource.druid.initial-size = 10
spring.datasource.druid.min-idle = 10
spring.datasource.druid.maxActive = 40
# 配置获取连接等待超时的时间-1min
spring.datasource.druid.maxWait = 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.druid.timeBetweenEvictionRunsMillis = 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.druid.minEvictableIdleTimeMillis = 300000
spring.datasource.druid.validationQuery = SELECT 1 FROM DUAL
spring.datasource.druid.testWhileIdle = true
spring.datasource.druid.testOnBorrow = false
spring.datasource.druid.testOnReturn = false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.druid.poolPreparedStatements = true
spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize = 20

# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.druid.filters = stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.druid.connectionProperties = druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 配置DruidStatFilter
spring.datasource.druid.web-stat-filter.enabled = true
spring.datasource.druid.web-stat-filter.url-pattern = /druid/*
#过滤器-静态资源放行
spring.datasource.druid.web-stat-filter.exclusions = *.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*

# 配置DruidStatViewServlet
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*
# IP白名单(没有配置或者为空,则允许所有访问)
spring.datasource.druid.stat-view-servlet.allow = 127.0.0.1,192.168.8.109
# IP黑名单 (存在共同时,deny优先于allow)
spring.datasource.druid.stat-view-servlet.deny = 192.168.1.188
#  禁用HTML页面上的“Reset All”功能
spring.datasource.druid.stat-view-servlet.reset-enable = false
# 登录名
spring.datasource.druid.stat-view-servlet.login-username = root
# 登录密码
spring.datasource.druid.stat-view-servlet.login-password = 123456

需要一个配置类来对应druid配置文件。
DruidProperties.java

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.util.Properties;

@Configuration
@PropertySource({"classpath:druid.properties"})
@ConfigurationProperties(prefix = "spring.datasource.druid")
public class DruidProperties {
    @Value("${spring.datasource.type}")
    private String type;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;

    //连接池信息
    private Integer initialSize;
    private Integer minIdle;
    private Integer maxActive;
    private Integer maxWait;
    private Integer timeBetweenEvictionRunsMillis;
    private Integer minEvictableIdleTimeMillis;
    private String validationQuery;
    private Boolean testWhileIdle;
    private Boolean testOnBorrow;
    private Boolean testOnReturn;
    private Boolean poolPreparedStatements;
    private Integer maxPoolPreparedStatementPerConnectionSize;
    private String filters;
    private Properties connectionProperties;

    //wrap Properties for webStatFilter and statViewServlet
    private Properties webStatFilter;
    private Properties statViewServlet;

    private String webStatFilterEnabled;
    private String webStatFilterUrlPattern;
    private String webStatFilterExclusions;

    private String statViewServletUrlPattern;
    private String statViewServletAllow;
    private String statViewServletDeny;
    private String statViewServletResetEnable;
    private String statViewServletLoginUsername;
    private String statViewServletLoginPassword;

    public String getWebStatFilterEnabled() {
        return webStatFilter.getProperty("enabled");
    }

    public String getWebStatFilterUrlPattern() {
        return webStatFilter.getProperty("url-pattern");
    }

    public String getWebStatFilterExclusions() {
        return webStatFilter.getProperty("exclusions");
    }

    public String getStatViewServletUrlPattern() {
        return statViewServlet.getProperty("url-pattern");
    }

    public String getStatViewServletAllow() {
        return statViewServlet.getProperty("allow");
    }

    public String getStatViewServletDeny() {
        return statViewServlet.getProperty("deny");
    }

    public String getStatViewServletResetEnable() {
        return statViewServlet.getProperty("reset-enable");
    }

    public String getStatViewServletLoginUsername() {
        return statViewServlet.getProperty("login-username");
    }

    public String getStatViewServletLoginPassword() {
        return statViewServlet.getProperty("login-password");
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

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

    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 Integer getInitialSize() {
        return initialSize;
    }

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

    public Integer getMinIdle() {
        return minIdle;
    }

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

    public Integer getMaxActive() {
        return maxActive;
    }

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

    public Integer getMaxWait() {
        return maxWait;
    }

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

    public Integer getTimeBetweenEvictionRunsMillis() {
        return timeBetweenEvictionRunsMillis;
    }

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

    public Integer getMinEvictableIdleTimeMillis() {
        return minEvictableIdleTimeMillis;
    }

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

    public String getValidationQuery() {
        return validationQuery;
    }

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

    public Boolean getTestWhileIdle() {
        return testWhileIdle;
    }

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

    public Boolean getTestOnBorrow() {
        return testOnBorrow;
    }

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

    public Boolean getTestOnReturn() {
        return testOnReturn;
    }

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

    public Boolean getPoolPreparedStatements() {
        return poolPreparedStatements;
    }

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

    public Integer getMaxPoolPreparedStatementPerConnectionSize() {
        return maxPoolPreparedStatementPerConnectionSize;
    }

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

    public String getFilters() {
        return filters;
    }

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

    public Properties getConnectionProperties() {
        return connectionProperties;
    }

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

    public Properties getWebStatFilter() {
        return webStatFilter;
    }

    public void setWebStatFilter(Properties webStatFilter) {
        this.webStatFilter = webStatFilter;
    }

    public Properties getStatViewServlet() {
        return statViewServlet;
    }

    public void setStatViewServlet(Properties statViewServlet) {
        this.statViewServlet = statViewServlet;
    }

    public void setWebStatFilterEnabled(String webStatFilterEnabled) {
        this.webStatFilterEnabled = webStatFilterEnabled;
    }

    public void setWebStatFilterUrlPattern(String webStatFilterUrlPattern) {
        this.webStatFilterUrlPattern = webStatFilterUrlPattern;
    }

    public void setWebStatFilterExclusions(String webStatFilterExclusions) {
        this.webStatFilterExclusions = webStatFilterExclusions;
    }

    public void setStatViewServletUrlPattern(String statViewServletUrlPattern) {
        this.statViewServletUrlPattern = statViewServletUrlPattern;
    }

    public void setStatViewServletAllow(String statViewServletAllow) {
        this.statViewServletAllow = statViewServletAllow;
    }

    public void setStatViewServletDeny(String statViewServletDeny) {
        this.statViewServletDeny = statViewServletDeny;
    }

    public void setStatViewServletResetEnable(String statViewServletResetEnable) {
        this.statViewServletResetEnable = statViewServletResetEnable;
    }

    public void setStatViewServletLoginUsername(String statViewServletLoginUsername) {
        this.statViewServletLoginUsername = statViewServletLoginUsername;
    }

    public void setStatViewServletLoginPassword(String statViewServletLoginPassword) {
        this.statViewServletLoginPassword = statViewServletLoginPassword;
    }

    public void setTimeBetweenConnectErrorMillis(Integer timeBetweenEvictionRunsMillis) {
    }
}

使用配置类把druid数据源注入到spring中。
DruidDataSourceConfig.java

import java.sql.SQLException;

@Configuration
public class DruidDataSourceConfig {
    @Autowired
    private DruidProperties dataSourceConfig;

    @Bean
    public DruidDataSource druidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        //数据源连接参数配置
        druidDataSource.setDriverClassName(dataSourceConfig.getDriverClassName());
        druidDataSource.setUrl(dataSourceConfig.getUrl());
        druidDataSource.setUsername(dataSourceConfig.getUsername());
        druidDataSource.setPassword(dataSourceConfig.getPassword());
        //连接池参数设置
        druidDataSource.setInitialSize(dataSourceConfig.getInitialSize());
        druidDataSource.setMinIdle(dataSourceConfig.getMinIdle());
        druidDataSource.setMaxActive(dataSourceConfig.getMaxActive());
        druidDataSource.setTimeBetweenConnectErrorMillis(dataSourceConfig.getTimeBetweenEvictionRunsMillis());
        druidDataSource.setMinEvictableIdleTimeMillis(dataSourceConfig.getMinEvictableIdleTimeMillis());
        druidDataSource.setValidationQuery(dataSourceConfig.getValidationQuery());
        druidDataSource.setTestWhileIdle(dataSourceConfig.getTestWhileIdle());
        druidDataSource.setTestOnBorrow(dataSourceConfig.getTestOnBorrow());
        druidDataSource.setTestOnReturn(dataSourceConfig.getTestOnReturn());
        druidDataSource.setPoolPreparedStatements(dataSourceConfig.getPoolPreparedStatements());
        druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(dataSourceConfig.getMaxPoolPreparedStatementPerConnectionSize());
        try {
            druidDataSource.setFilters(dataSourceConfig.getFilters());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        druidDataSource.setConnectProperties(dataSourceConfig.getConnectionProperties());
        return druidDataSource;
    }

    /**
     * Druid监控servlet配置
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),dataSourceConfig.getStatViewServletUrlPattern());
        //urlPattern-访问路径设置
        //IP配置
        servletRegistrationBean.addInitParameter("allow",dataSourceConfig.getStatViewServletAllow());//IP白名单
        servletRegistrationBean.addInitParameter("deny",dataSourceConfig.getStatViewServletDeny());//IP黑名单
        //登陆账户配置
        servletRegistrationBean.addInitParameter("loginUsername",dataSourceConfig.getStatViewServletLoginUsername());
        servletRegistrationBean.addInitParameter("loginPassword",dataSourceConfig.getStatViewServletLoginPassword());
        //是否允许Reset All-计数器清零操作
        servletRegistrationBean.addInitParameter("resetEnable",dataSourceConfig.getStatViewServletResetEnable());
        return servletRegistrationBean;
    }


    /**
     * Druid监控过滤器配置
     * @return
     */
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        //设置过滤器
        filterRegistrationBean.addUrlPatterns(dataSourceConfig.getWebStatFilterUrlPattern());//url过滤规则
        filterRegistrationBean.addInitParameter("enabled",dataSourceConfig.getWebStatFilterEnabled());//是否开启过滤功能
        filterRegistrationBean.addInitParameter("exclusions",dataSourceConfig.getWebStatFilterExclusions());//忽略
        return filterRegistrationBean;
    }

}

写一个查看数据源是否注入的类:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

@Component
public class DataSourceShow implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // Spring Boot 默认已经配置好了数据源,程序员可以直接 DI 注入然后使用即可
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        System.out.println("--------------------------------");
        System.out.println(dataSource.getClass().getName());
        System.out.println("--------------------------------");

    }
}

实体类:

public class User {
    /**
     * 用户ID
     */
    private String userId;

    /**
     * 用户账号
     */
    private String userName;

    private int age;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

mapper类:

import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper {
    public User getUser(String id);
}

service接口及实现类:

public interface UserService {
    public User getUser(String id);
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUser(String id) {
        User user = userMapper.getUser(id);
        return user;
    }
}

controller类:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    public UserService userService;

    @RequestMapping("/getUserInfo")
    @ResponseBody
    public User getUserInfo(String id){
        User user = userService.getUser(id);
        return user;
    }
}

mybatis配置类:

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = {"mapper类所在包名"}, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MybaticsConfig {

    /**
     * 配置事务管理器,不然事务不起作用
     *
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource druidDataSource) {
        return new DataSourceTransactionManager(druidDataSource);
    }

	//将@Bean注入的dataSource注入,名称就是注入dataSource方法的方法名
    @Primary
    @Bean(name = "masterSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(DataSource druidDataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(druidDataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/*.xml"));
        sqlSessionFactoryBean.setTypeAliasesPackage("com.dmf.demo.mybatics");
        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
        return sqlSessionFactoryBean.getObject();
    }
}

mapper文件;


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="包名.UserMapper">
    <resultMap type="包名.User" id="UserInfo">
        <id property="userId" column="id"/>
        <result  property="userName" column="name"/>
        <result  property="age" column="age"/>
    resultMap>
    <select id="getUser" resultMap="UserInfo">
        select * from user where id = #{id}
    select>
mapper>

建表语句:

CREATE TABLE `user` (
`id`  varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`name`  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`age`  int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT
;

application.yml

server:
  servlet:
    context-path: /demo
  port: 8080



mybatis:
  mapper-locations: classpath*:mapper/**/*Mapper.xml
    # 使用驼峰命名
  # 数据库表列:user_name
  # 实体类属性:userName
  configuration:
    map-underscore-to-camel-case: true

springboot启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;


@EnableConfigurationProperties
@SpringBootApplication(scanBasePackages = "包名")
public class DataSourceDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DataSourceDemoApplication.class, args);
    }

}

启动springboot
Spring Boot学习笔记(十一)Druid数据源及动态数据源配置_第1张图片
可以看到druid数据源注入成功。也可以访问controller测试数据源是否配置成功。
在浏览器访问:http://localhost:8080/demo/druid,可以查看druid的控制台页面(账号密码看配置文件:root/123456)。

二、使用SpringBoot的自动配置配置数据源

使用druid的starter包替换druid包:

	
		<dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.10version>
        dependency>

在application.yml文件中新增druid配置:

spring:
  datasource:
    # 数据源基本配置
    username: 账号
    password: 密码
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    # driver-class需要注意mysql驱动的版本(com.mysql.cj.jdbc.Driver 或 com.mysql.jdbc.Driver)
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    # Druid的其他属性配置
    druid:
      # 初始化时建立物理连接的个数
      initial-size: 10
      # 连接池的最小空闲数量
      min-idle: 5
      # 连接池最大连接数量
      max-active: 20
      # 获取连接时最大等待时间,单位毫秒
      max-wait: 60000
      # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      test-while-idle: true
      # 既作为检测的间隔时间又作为testWhileIdel执行的依据
      time-between-eviction-runs-millis: 60000
      # 销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接(配置连接在池中的最小生存时间)
      min-evictable-idle-time-millis: 30000
      # 用来检测数据库连接是否有效的sql 必须是一个查询语句(oracle中为 select 1 from dual)
      validation-query: SELECT 1 FROM DUAL
      # 申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
      test-on-borrow: false
      # 归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
      test-on-return: false
      # 是否缓存preparedStatement, 也就是PSCache,PSCache对支持游标的数据库性能提升巨大,比如说oracle,在mysql下建议关闭。
      pool-prepared-statements: false
      # 置监控统计拦截的filters,去掉后监控界面sql无法统计,stat: 监控统计、Slf4j:日志记录、waLL: 防御sqL注入
      filters: stat,wall,slf4j
      # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      max-pool-prepared-statement-per-connection-size: -1
      # 合并多个DruidDataSource的监控数据
      use-global-data-source-stat: true
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      web-stat-filter:
        # 是否启用StatFilter默认值true
        enabled: true
        # 添加过滤规则
        url-pattern: /*
        # 忽略过滤的格式
        exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico

      stat-view-servlet:
        # 是否启用StatViewServlet默认值true
        enabled: true
        # 访问路径为/druid时,跳转到StatViewServlet
        url-pattern: /druid/*
        # 是否能够重置数据
        reset-enable: false
        # 需要账号密码才能访问控制台,默认为root
        login-username: root
        login-password: 123456
        # IP白名单
        allow: 127.0.0.1
        # IP黑名单(共同存在时,deny优先于allow)
        deny:

使用这种方法我们就无需再写配置属性类以及配置类了。自动配置类以及帮我们完成注入,直接使用就可以了。
druid自动配置类DruidDataSourceAutoConfigure.java源码:

package com.alibaba.druid.spring.boot.autoconfigure;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidFilterConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidStatViewServletConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidWebStatFilterConfiguration;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@ConditionalOnClass({DruidDataSource.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
    private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);

    public DruidDataSourceAutoConfigure() {
    }

    @Bean(
        initMethod = "init"
    )
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        LOGGER.info("Init DruidDataSource");
        return new DruidDataSourceWrapper();
    }
}

通过源码可以看到注入了dataSource,名字为“dataSource”。那我们在配置mybatis的SqlSessionFactory或者事务管理器PlatformTransactionManager时,直接使用名称“dataSource”就可以使用了。

三、动态数据源配置

在实际项目中,难免会碰到使用多个数据源的情况,不使用动态数据源的话,就需要写多套mybatis代码,这样太麻烦了。spring提供了AbstractRoutingDataSource类,我们继承这个类就可以实现动态数据源了。

多数据源枚举类:

public enum DBTypeEnum {
    /**
     * 主库
     */
    MASTER("master"),

    /**
     * 从库
     */
    SLAVE("slave");

    private final String value;

    DBTypeEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

}

上下文数据源类
DbContextHolder.java

public class DbContextHolder {
	//使用ThreadLocal,防止线程安全问题
    private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal();

    /**
     * 设置数据源
     *
     * @param dbTypeEnum 数据库类型
     */
    public static void setDbType(DBTypeEnum dbTypeEnum) {
        CONTEXT_HOLDER.set(dbTypeEnum.getValue());
    }

    /**
     * 取得当前数据源
     *
     * @return dbType
     */
    public static String getDbType() {
        return (String) CONTEXT_HOLDER.get();
    }

    /**
     * 清除上下文数据
     */
    public static void clearDbType() {
        CONTEXT_HOLDER.remove();
    }

}

动态数据源类:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDbType();
    }
}

再使用DynamicDataSource时,会调用determineCurrentLookupKey方法拿到具体数据源的key,然后使用对应的数据源,如果没有对应的数据源,会使用DynamicDataSource设置的默认数据源
applicationyml配置:

spring:
  datasource:
    # 数据源基本配置
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    # driver-class需要注意mysql驱动的版本(com.mysql.cj.jdbc.Driver 或 com.mysql.jdbc.Driver)
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    # Druid的其他属性配置
    druid:
      # 初始化时建立物理连接的个数
      initial-size: 10
      # 连接池的最小空闲数量
      min-idle: 5
      # 连接池最大连接数量
      max-active: 20
      # 获取连接时最大等待时间,单位毫秒
      max-wait: 60000
      # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      test-while-idle: true
      # 既作为检测的间隔时间又作为testWhileIdel执行的依据
      time-between-eviction-runs-millis: 60000
      # 销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接(配置连接在池中的最小生存时间)
      min-evictable-idle-time-millis: 30000
      # 用来检测数据库连接是否有效的sql 必须是一个查询语句(oracle中为 select 1 from dual)
      validation-query: SELECT 1 FROM DUAL
      # 申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
      test-on-borrow: false
      # 归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
      test-on-return: false
      # 是否缓存preparedStatement, 也就是PSCache,PSCache对支持游标的数据库性能提升巨大,比如说oracle,在mysql下建议关闭。
      pool-prepared-statements: false
      # 置监控统计拦截的filters,去掉后监控界面sql无法统计,stat: 监控统计、Slf4j:日志记录、waLL: 防御sqL注入
      filters: stat,wall,slf4j
      # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      max-pool-prepared-statement-per-connection-size: -1
      # 合并多个DruidDataSource的监控数据
      use-global-data-source-stat: true
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      web-stat-filter:
        # 是否启用StatFilter默认值true
        enabled: true
        # 添加过滤规则
        url-pattern: /*
        # 忽略过滤的格式
        exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico

      stat-view-servlet:
        # 是否启用StatViewServlet默认值true
        enabled: true
        # 访问路径为/druid时,跳转到StatViewServlet
        url-pattern: /druid/*
        # 是否能够重置数据
        reset-enable: false
        # 需要账号密码才能访问控制台,默认为root
        login-username: root
        login-password: 123456
        # IP白名单
        allow: 127.0.0.1
        # IP黑名单(共同存在时,deny优先于allow)
        deny:

    master:
      # 数据源基本配置
      username: 账号
      password: 密码
      url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
      # driver-class需要注意mysql驱动的版本(com.mysql.cj.jdbc.Driver 或 com.mysql.jdbc.Driver)
      driver-class-name: com.mysql.jdbc.Driver

    slave:
      # 数据源基本配置
      username: root
      password: root1234
      url: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
      # driver-class需要注意mysql驱动的版本(com.mysql.cj.jdbc.Driver 或 com.mysql.jdbc.Driver)
      driver-class-name: com.mysql.jdbc.Driver

数据源配置类:

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource(){
        return new DruidDataSourceBuilder().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource(){
        return new DruidDataSourceBuilder().build();
    }

    @Bean
    @Primary
    public DataSource multipleDataSource(DataSource masterDataSource,DataSource slaveDataSource){

        DynamicDataSource multipleDataSource = new DynamicDataSource();
        Map<Object, Object> dataSources = new HashMap<>();
        dataSources.put(DBTypeEnum.MASTER.getValue(), masterDataSource);
        dataSources.put(DBTypeEnum.SLAVE.getValue(), slaveDataSource);
        multipleDataSource.setTargetDataSources(dataSources);
        multipleDataSource.setDefaultTargetDataSource(masterDataSource);

        return multipleDataSource;
    }
}

数据源注册成功就可以使用“multipleDataSource”注入使用了。将mybatis配置注入的数据源名称改成“multipleDataSource”就可以使用了。
这样还是不够灵活,每次在调用数据库时,还需要设置DbContextHolder的CONTEXT_HOLDER属性到具体的数据源。
可以使用aop的方式实现数据源切换,只需一个注解即可。

自定义注解类:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.METHOD
})
public @interface SwitchDataSource {
    DBTypeEnum value() default DBTypeEnum.MASTER;
}

aop处理类:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * AOP方式动态切换数据源 (为了保证AOP在事务注解之前生效,Order的值越小,优先级越高)
 */
@Component
@Aspect
@Order(-100)
public class DataSourceSwitchAspect {

    @Pointcut("@annotation(SwitchDataSource)")
    public void pointcut() {
    }

    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint){

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        SwitchDataSource dataSourceSwitch = methodSignature.getMethod().getAnnotation(SwitchDataSource.class);
        System.out.println("使用数据源:"+dataSourceSwitch.value());

        DbContextHolder.setDbType(dataSourceSwitch.value());
    }

    @After(value = "pointcut()")
    public void after(JoinPoint joinPoint){
        DbContextHolder.clearDbType();
    }
}

然后在service里使用@SwitchDataSource注解即可实现动态切换数据源。

	@Override
    @SwitchDataSource(DBTypeEnum.MASTER)
    public User getUser(String id) {
        User user = userMapper.getUser(id);
        return user;
    }

    @Override
    @SwitchDataSource(DBTypeEnum.SLAVE)
    public User getUser2(String id){
        User user = userMapper.getUser(id);

        return user;
    }

你可能感兴趣的:(Spring,Boot,spring,boot,学习,java,druid,数据源)