springboot整合druid及多数据源配置

前言

本篇主要分两部分 ①springboot整合druid的代码配置,以及druid的监控页面演示;②对实际场景中多数据源的配置使用进行讲解。

一、springboot整合druid的演示demo

可以用idea快速生成一个可运行的demo工程,具体可以参考如何快速创建springboot项目

  • 主要用到的依赖如下:
      org.springframework.boot spring-boot-starter-data-jdbc   mysql mysql-connector-java runtime   com.alibaba druid 1.2.17   log4j log4j 1.2.17   org.springframework.boot spring-boot-starter-jdbc   org.springframework.boot spring-boot-starter-web 
    •  配置数据库需要的配置文件application.yml(注意格式和空格)
      spring:
        datasource:
          username: root
          password: root
          url: jdbc:mysql://localhost:3306/firsttest?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
          driver-class-name: com.mysql.jdbc.Driver
          type: com.alibaba.druid.pool.DruidDataSource
          poolPreparedStatements: true
           #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
           #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
       #则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j     filters: stat,wall,log4j
          maxPoolPreparedStatementPerConnectionSize: 20
          useGlobalDataSourceStat: true
          connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
      
      • 编写durid的启动配置类,加载数据库信息,和druid监控页面的初始化工作 
        package com.czing.jdbcdemo.config;
        import com.alibaba.druid.pool.DruidDataSource;
        import com.alibaba.druid.support.http.StatViewServlet;
        import com.alibaba.druid.support.http.WebStatFilter;
        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.servlet.Servlet;
        import javax.sql.DataSource;
        import java.util.Arrays;
        import java.util.HashMap;
        import java.util.Map;
        /**
         * @Description TODO
         * @Author wangchengzhi
         * @Date 2023/5/10 21:53
         */
        @Configuration
        public class DruidConfig {
                @ConfigurationProperties(prefix = "spring.datasource")
                @Bean
                public DataSource druidDataSource(){
                    return new DruidDataSource();
                }
            /**
             * @Author wangchengzhi
             * @Description
             * 向DruidConfig中添加代码,配置druid监控管理台的servlet
             * @Date 22:12 2023/5/10
             * @Param
             * @return
             **/
            @Bean
            public ServletRegistrationBean druidServletRegistrationBean(){
                ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
                Map initParams = new HashMap<>();
                initParams.put("loginUsername","root");
                initParams.put("loginPassword","root");
                //后台允许谁可以访问
                //initParams.put("allow", "localhost"):表示只有本机可以访问
                //initParams.put("allow", ""):为空或者为null时,表示允许所有访问
                initParams.put("allow","");
                //deny:Druid 后台拒绝谁访问
                //initParams.put("msb", "192.168.1.20");表示禁止此ip访问
                servletRegistrationBean.setInitParameters(initParams);
                return servletRegistrationBean;
            }
            @Bean
            public FilterRegistrationBean webStatFilter() {
                FilterRegistrationBean bean = new FilterRegistrationBean();
                bean.setFilter(new WebStatFilter());
                //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
                Map initParams = new HashMap<>();
                initParams.put("exclusions", "*.js,*.css,/druid/*");
                bean.setInitParameters(initParams);
                //"/*" 表示过滤所有请求
                bean.setUrlPatterns(Arrays.asList("/*"));
                return bean;
            }
        }
        
        •  编写controller类进行测试(此处使用jdbcTemplate进行演示)
          package com.czing.jdbcdemo.controller;
          import com.czing.jdbcdemo.mult.DataSource;
          import com.czing.jdbcdemo.mult.DataSourceType;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.jdbc.core.JdbcTemplate;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RestController;
          import java.util.List;
          import java.util.Map;
          /**
           * @Description TODO
           * @Author wangchengzhi
           * @Date 2023/5/10 17:12
           */
          @RestController
          public class JDBCController {
              @Autowired
              JdbcTemplate jdbcTemplate;
              /**
               * @Author wangchengzhi
               * @Description
               * 使用springboot自带的jdbcTemplate实现数据库操作
               * @Date 21:34 2023/5/10
               * @Param
               * @return
               **/
              @RequestMapping("/selectTest")
              public List> getTestAccount(){
                  String sql ="select * from account";
                  List> maps = jdbcTemplate.queryForList(sql);
                  return maps;
              }
          }
          
          •  启动项目,演示效果

            项目启动日志: 

             

            druid的监控页面访问 http://localhost:8080/druid/sql.html

             访问编写的controller测试类:http://localhost:8080/selectTest(访问之后可以观察druid监控页面的情况)

             

            二、druid多数据源的配置使用

            • 修改application.yml文件 

              spring:
                datasource:
                  local:
                    username: root
                    password: root
                    #注意多数据源配置的时候此处为jdbc-url
                    jdbc-url: jdbc:mysql://localhost:3306/firsttest?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
                    driver-class-name: com.mysql.jdbc.Driver
                    type: com.alibaba.druid.pool.DruidDataSource
                  remote:
                    username: root
                    password: root
                    jdbc-url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
                    driver-class-name: com.mysql.jdbc.Driver
                    type: com.alibaba.druid.pool.DruidDataSource
              

               

               编写多数据源的配置类:

              package com.czing.jdbcdemo.config;
              import com.czing.jdbcdemo.mult.DataSourceType;
              import com.czing.jdbcdemo.mult.DynamicDataSource;
              import org.springframework.boot.context.properties.ConfigurationProperties;
              import org.springframework.boot.jdbc.DataSourceBuilder;
              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;
              /**
               * @Description TODO
               * @Author wangchengzhi
               * @Date 2023/5/10 23:07
               */
              @Configuration
              public class DataSourceConfig {
                  @Bean
                  @ConfigurationProperties("spring.datasource.remote")
                  public DataSource remoteDataSource() {
                      return DataSourceBuilder.create().build();
                  }
                  @Bean
                  @ConfigurationProperties("spring.datasource.local")
                  public DataSource localDataSource() {
                      return DataSourceBuilder.create().build();
                  }
                  @Bean(name = "dynamicDataSource")
                  @Primary
                  public DynamicDataSource dataSource(DataSource remoteDataSource, DataSource localDataSource) {
                      Map targetDataSources = new HashMap<>();
                      targetDataSources.put(DataSourceType.REMOTE.name(), remoteDataSource);
                      targetDataSources.put(DataSourceType.LOCAL.name(), localDataSource);
                      return new DynamicDataSource(remoteDataSource, targetDataSources);
                  }
              }
              

               使用自定义注解,在查询方法的时候指定对应的数据源:

              • 编写枚举类,用于指定数据源
                package com.czing.jdbcdemo.mult;
                public enum DataSourceType {
                    REMOTE,
                    LOCAL
                }
                
                • 数据源切换的处理类
                  package com.czing.jdbcdemo.mult;
                  /**
                   * @Description TODO
                   * @Author wangchengzhi
                   * @Date 2023/5/10 22:57
                   */
                  public class DynamicDataSourceContextHolder {
                      /**
                       * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
                       *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
                       */
                      private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();
                      /**
                       * 设置数据源变量
                       * @param dataSourceType
                       */
                      public static void setDataSourceType(String dataSourceType){
                          System.out.printf("切换到{%s}数据源", dataSourceType);
                          CONTEXT_HOLDER.set(dataSourceType);
                      }
                      /**
                       * 获取数据源变量
                       * @return
                       */
                      public static String getDataSourceType(){
                          return CONTEXT_HOLDER.get();
                      }
                      /**
                       * 清空数据源变量
                       */
                      public static void clearDataSourceType(){
                          CONTEXT_HOLDER.remove();
                      }
                  }
                  
                  • 数据源信息加载的实现
                    package com.czing.jdbcdemo.mult;
                    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
                    import javax.sql.DataSource;
                    import java.util.Map;
                    /**
                     * @Description TODO
                     * @Author wangchengzhi
                     * @Date 2023/5/10 23:03
                     */
                    public class DynamicDataSource extends AbstractRoutingDataSource {
                        public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {
                            super.setDefaultTargetDataSource(defaultTargetDataSource);
                            super.setTargetDataSources(targetDataSources);
                            // afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
                            super.afterPropertiesSet();
                        }
                        /**
                         * @Author wangchengzhi
                         * @Description
                         * 根据Key获取数据源的信息
                         * @Date 23:06 2023/5/10
                         * @Param
                         * @return
                         **/
                        @Override
                        protected Object determineCurrentLookupKey() {
                            return DynamicDataSourceContextHolder.getDataSourceType();
                        }
                    }
                    
                    • 多数据源的配置启动类,注入数据库配置信息
                      package com.czing.jdbcdemo.config;
                      import com.czing.jdbcdemo.mult.DataSourceType;
                      import com.czing.jdbcdemo.mult.DynamicDataSource;
                      import org.springframework.boot.context.properties.ConfigurationProperties;
                      import org.springframework.boot.jdbc.DataSourceBuilder;
                      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;
                      /**
                       * @Description TODO
                       * @Author wangchengzhi
                       * @Date 2023/5/10 23:07
                       */
                      @Configuration
                      public class DataSourceConfig {
                          @Bean
                          @ConfigurationProperties("spring.datasource.remote")
                          public DataSource remoteDataSource() {
                              return DataSourceBuilder.create().build();
                          }
                          @Bean
                          @ConfigurationProperties("spring.datasource.local")
                          public DataSource localDataSource() {
                              return DataSourceBuilder.create().build();
                          }
                          @Bean(name = "dynamicDataSource")
                          @Primary
                          public DynamicDataSource dataSource(DataSource remoteDataSource, DataSource localDataSource) {
                              Map targetDataSources = new HashMap<>();
                              targetDataSources.put(DataSourceType.REMOTE.name(), remoteDataSource);
                              targetDataSources.put(DataSourceType.LOCAL.name(), localDataSource);
                              return new DynamicDataSource(remoteDataSource, targetDataSources);
                          }
                      }
                      
                      • 自定义注解
                        package com.czing.jdbcdemo.mult;
                        import java.lang.annotation.*;
                        @Target(ElementType.METHOD)
                        @Retention(RetentionPolicy.RUNTIME)
                        @Documented
                        public @interface DataSource {
                            /**
                             * 切换数据源名称
                             */
                            DataSourceType value() default DataSourceType.REMOTE;
                        }
                        
                        • AOP拦截类的实现
                          package com.czing.jdbcdemo.mult;
                          import org.aspectj.lang.ProceedingJoinPoint;
                          import org.aspectj.lang.annotation.Around;
                          import org.aspectj.lang.annotation.Aspect;
                          import org.aspectj.lang.annotation.Pointcut;
                          import org.aspectj.lang.reflect.MethodSignature;
                          import org.springframework.core.annotation.Order;
                          import org.springframework.stereotype.Component;
                          import java.lang.reflect.Method;
                          /**
                           * @Description TODO
                           * @Author wangchengzhi
                           * @Date 2023/5/11 10:09
                           */
                          @Aspect
                          @Order(1)
                          @Component
                          public class DataSourceAspect {
                              @Pointcut("@annotation(com.czing.jdbcdemo.mult.DataSource)")
                              public void dsPointCut() {
                              }
                              @Around("dsPointCut()")
                              public Object around(ProceedingJoinPoint point) throws Throwable {
                                  MethodSignature signature = (MethodSignature) point.getSignature();
                                  Method method = signature.getMethod();
                                  DataSource dataSource = method.getAnnotation(DataSource.class);
                                  if (dataSource != null) {
                                      DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
                                  }
                                  try {
                                      return point.proceed();
                                  } finally {
                                      // 销毁数据源 在执行方法之后
                                      DynamicDataSourceContextHolder.clearDataSourceType();
                                  }
                              }
                          }
                          
                          • controller测试类的编写验证
                            package com.czing.jdbcdemo.controller;
                            import com.czing.jdbcdemo.mult.DataSource;
                            import com.czing.jdbcdemo.mult.DataSourceType;
                            import org.springframework.beans.factory.annotation.Autowired;
                            import org.springframework.jdbc.core.JdbcTemplate;
                            import org.springframework.web.bind.annotation.RequestMapping;
                            import org.springframework.web.bind.annotation.RestController;
                            import java.util.List;
                            import java.util.Map;
                            /**
                             * @Description TODO
                             * @Author wangchengzhi
                             * @Date 2023/5/10 17:12
                             */
                            @RestController
                            public class JDBCController {
                                @Autowired
                                JdbcTemplate jdbcTemplate;
                                /**
                                 * @Author wangchengzhi
                                 * @Description
                                 * 使用springboot自带的jdbcTemplate实现数据库操作
                                 * @Date 21:34 2023/5/10
                                 * @Param
                                 * @return
                                 **/
                                @RequestMapping("/selectTest")
                                @DataSource(value=DataSourceType.LOCAL)
                                public List> getTestAccount(){
                                    String sql ="select * from account";
                                    List> maps = jdbcTemplate.queryForList(sql);
                                    return maps;
                                }
                                /**
                                 * @Author wangchengzhi
                                 * @Description
                                 * 使用springboot自带的jdbcTemplate实现数据库操作
                                 * @Date 21:34 2023/5/10
                                 * @Param
                                 * @return
                                 **/
                                @RequestMapping("/selectRemote")
                                @DataSource(DataSourceType.REMOTE)
                                public List> getRemoteAccount(){
                                    String sql ="select * from account";
                                    List> maps = jdbcTemplate.queryForList(sql);
                                    return maps;
                                }
                            }
                            
                            •  启动项目,分别访问两个接口查询不同的数据源

                              http://localhost:8080/selectTest 的访问数据:

                              http://localhost:8080/selectRemote 的访问数据

                               结语

                              以上为springboot实现druid配置查询的demo,并且演示了如何实现多数据源配置动态切换的代码实现,注意多数据源配置的时候url的配置为jdbc-url,其他运行中遇到的问题欢迎留言交流,希望对您的学习有所帮助。