栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot配置Druid多数据源并解决循环依赖

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springboot配置Druid多数据源并解决循环依赖

我的springboot web项目工程目录如下:

POM文件如下:




  4.0.0

  cn.example
  SpringBootStudy
  1.0-SNAPSHOT
  war

  SpringBootStudy Maven Webapp
  
  http://www.example.com

  
    UTF-8
    1.8
    1.8

    
    1.1.16
  

  
  
    org.springframework.boot
    spring-boot-starter-parent
    2.3.3.RELEASE
  

  
  
    
      org.springframework.boot
      spring-boot-starter-web
    

    
    
      org.springframework.boot
      spring-boot-configuration-processor
      true
    

    
      org.projectlombok
      lombok
    

    
    
      org.springframework.boot
      spring-boot-starter-jdbc
    
    
    
      mysql
      mysql-connector-java
      runtime
    

    
    
      com.alibaba
      druid-spring-boot-starter
      ${druid.starter.version}
    

    
    
      org.springframework.boot
      spring-boot-starter-test
      test
    

    
    
      org.springframework.boot
      spring-boot-starter-aop
    
    
      org.aspectj
      aspectjrt
    
  

  
  
    
      
        org.springframework.boot
        spring-boot-maven-plugin
      
    
  


YAML配置文件如下:

spring:
  profiles:
    active: dev
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource  #修改数据源为Druid
    druid:
      master:
        url: jdbc:mysql://localhost:3306/study?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
        username: root
        password: root
      slave:
        enable: true
        url: jdbc:mysql://localhost:3306/study?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
        username: root
        password: root
      initialSize: 3  #修改Druid的默认连接配置
      minIdle: 10   # 最小连接池数量
      maxActive: 20   # 最大连接池数量
      maxWait: 60000   # 配置获取连接等待超时的时间
      timeBetweenEvictionRunsMillis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      minEvictableIdleTimeMillis: 300000  # 配置一个连接在池中最小生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000  # 配置一个连接在池中最大生存的时间,单位是毫秒

在annotation包中创建自定义注解DataSource:

package cn.xhh.study.common.annotation;

import cn.xhh.study.common.enums.DataSourcesType;

import java.lang.annotation.*;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
public @interface DataSource {
    DataSourcesType name() default DataSourcesType.MASTER;
}

在enums包中创建枚举类DataSourcesType:

package cn.xhh.study.common.enums;


public enum  DataSourcesType {
    
    MASTER,

    
    SLAVE
}

在core.aop包中创建切面类DynamicDataSourceAspect:

package cn.xhh.study.core.aop;

import cn.xhh.study.common.annotation.DataSource;
import cn.xhh.study.core.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Aspect
@Component
@Order(-1)
public class DynamicDataSourceAspect {

    protected Logger logger = LoggerFactory.getLogger(getClass());


    @Pointcut("@annotation(cn.xhh.study.common.annotation.DataSource)"
            + "|| @within(cn.xhh.study.common.annotation.DataSource)")
    public void dsPointCut()  {
    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Method targetMethod = this.getTargetMethod(point);
        //获取要切换的数据源
        DataSource dataSource = targetMethod.getAnnotation(DataSource.class);
        if (dataSource != null)  {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.name().name());
        }
        try {
            return point.proceed();
        }
        finally  {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.removeDataSourceType();
        }
    }

    
    private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method agentMethod = methodSignature.getMethod();
        return pjp.getTarget().getClass().getMethod(agentMethod.getName(), agentMethod.getParameterTypes());
    }
}

在core.datasource包中创建动态数据源类DynamicDataSource和数据源切换处理类DynamicDataSourceContextHolder:

package cn.xhh.study.core.datasource;

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


public class DynamicDataSource extends AbstractRoutingDataSource {

    public static  DynamicDataSource build() {
        return new DynamicDataSource();
    }

    
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }

}

package cn.xhh.study.core.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class DynamicDataSourceContextHolder {

    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    
    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    
    public static void setDataSourceType(String dataSourceType) {
        log.info("已切换到{}数据源", dataSourceType);
        contextHolder.set(dataSourceType);
    }

    
    public static String getDataSourceType() {
        return contextHolder.get();
    }

    
    public static void removeDataSourceType() {
        contextHolder.remove();
    }

}

在core.druid包中创建数据源配置类DataSourceConfiguration和数据源配置文件类DataSourceProperties:

package cn.xhh.study.core.druid;


import cn.xhh.study.common.enums.DataSourcesType;
import cn.xhh.study.core.datasource.DynamicDataSource;
import cn.xhh.study.core.druid.DataSourceProperties;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;

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


@Configuration
public class DataSourceConfiguration {

    
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.setDataSource(DruidDataSourceBuilder.create().build());
    }

    
    @Bean
    @ConditionalOnProperty( prefix = "spring.datasource.druid.slave", name = "enable", havingValue = "true")//是否开启数据源开关---若不开启 默认适用默认数据源
    @ConfigurationProperties("spring.datasource.druid.slave")
    public DataSource slaveDataSource(DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.setDataSource(DruidDataSourceBuilder.create().build());
    }

    
    @Bean(name = "dynamicDataSource")
    @Primary
    @DependsOn({"masterDataSource","slaveDataSource"})
    public DynamicDataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {
        Map targetDataSources = new HashMap<>();
        DynamicDataSource dynamicDataSource = DynamicDataSource.build();
        targetDataSources.put(DataSourcesType.MASTER.name(), masterDataSource);
        targetDataSources.put(DataSourcesType.SLAVE.name(), slaveDataSource);
        //默认数据源配置 DefaultTargetDataSource
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        //额外数据源配置 TargetDataSources
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.afterPropertiesSet();
        return dynamicDataSource;
    }
}

package cn.xhh.study.core.druid;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;


@Setter
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.druid")
public class DataSourceProperties {

    private int initialSize;

    private int minIdle;

    private int maxActive;

    private int maxWait;

    private int timeBetweenEvictionRunsMillis;

    private int minEvictableIdleTimeMillis;

    private int maxEvictableIdleTimeMillis;

    private String validationQuery;

    private boolean testWhileIdle;

    private boolean testOnBorrow;

    private boolean testOnReturn;

    public DruidDataSource setDataSource(DruidDataSource datasource) {

        // 配置初始化大小、最小、最大
        datasource.setInitialSize(initialSize);
        datasource.setMaxActive(maxActive);
        datasource.setMinIdle(minIdle);

        // 配置获取连接等待超时的时间
        datasource.setMaxWait(maxWait);

        // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

        // 配置一个连接在池中最小、最大生存的时间,单位是毫秒
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);

        
        datasource.setValidationQuery(validationQuery);

        
        datasource.setTestWhileIdle(testWhileIdle);

        
        datasource.setTestOnBorrow(testOnBorrow);

        
        datasource.setTestOnReturn(testOnReturn);

        return datasource;
    }
}

在启动类StudyApplication加入注解,解决循环依赖:

package cn.xhh.study;

import cn.xhh.study.servlet.listener.MyListener;
import cn.xhh.study.servlet.MyServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;


@ServletComponentScan
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class StudyApplication {
    public static void main(String[] args) {
        SpringApplication.run(StudyApplication.class, args);
    }
}

添加Controller测试类:

package cn.xhh.study.controller;

import cn.xhh.study.common.annotation.DataSource;
import cn.xhh.study.common.enums.DataSourcesType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;


@Controller
public class JdbcTemplateController {

    private JdbcTemplate jdbcTemplate;

    public JdbcTemplateController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @ResponseBody
    @RequestMapping("queryUserForList")
    @DataSource(name = DataSourcesType.SLAVE)
    public List> getData() {
        String sql = "select * from user";
        List> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
}

自己在mysql中建立一个user表,进行测试。

在浏览器输入地址,查看结果:

http://localhost:8081/queryUserForList

后台程序打印日志:

2021-12-08 10:58:13.400  INFO 7284 --- [nio-8081-exec-1] c.x.s.c.d.DynamicDataSourceContextHolder : 已切换到SLAVE数据源

Druid多数据源配置成功!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/643355.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号