国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SpringBoot整合Druid數(shù)據(jù)庫連接池&多數(shù)據(jù)源&注解切換&動態(tài)添加

這篇具有很好參考價值的文章主要介紹了SpringBoot整合Druid數(shù)據(jù)庫連接池&多數(shù)據(jù)源&注解切換&動態(tài)添加。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

1. 引入依賴

            <!-- 阿里數(shù)據(jù)庫連接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.2.16</version>
            </dependency>

            <!-- Mysql驅(qū)動包 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>

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

spring:
  datasource:
    # 數(shù)據(jù)源基本配置
    username: 賬號
    password: 密碼
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    # driver-class需要注意mysql驅(qū)動的版本(com.mysql.cj.jdbc.Driver 或 com.mysql.jdbc.Driver)
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    # Druid的其他屬性配置
    druid:
      # 初始化時建立物理連接的個數(shù)
      initial-size: 10
      # 連接池的最小空閑數(shù)量
      min-idle: 5
      # 連接池最大連接數(shù)量
      max-active: 20
      # 獲取連接時最大等待時間,單位毫秒
      max-wait: 60000
      # 申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測連接是否有效。
      test-while-idle: true
      # 既作為檢測的間隔時間又作為testWhileIdel執(zhí)行的依據(jù)
      time-between-eviction-runs-millis: 60000
      # 銷毀線程時檢測當(dāng)前連接的最后活動時間和當(dāng)前時間差大于該值時,關(guān)閉當(dāng)前連接(配置連接在池中的最小生存時間)
      min-evictable-idle-time-millis: 30000
      # 用來檢測數(shù)據(jù)庫連接是否有效的sql 必須是一個查詢語句(oracle中為 select 1 from dual)
      validation-query: SELECT 1 FROM DUAL
      # 申請連接時會執(zhí)行validationQuery檢測連接是否有效,開啟會降低性能,默認(rèn)為true
      test-on-borrow: false
      # 歸還連接時會執(zhí)行validationQuery檢測連接是否有效,開啟會降低性能,默認(rèn)為true
      test-on-return: false
      # 是否緩存preparedStatement, 也就是PSCache,PSCache對支持游標(biāo)的數(shù)據(jù)庫性能提升巨大,比如說oracle,在mysql下建議關(guān)閉。
      pool-prepared-statements: false
      # 置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計,stat: 監(jiān)控統(tǒng)計、Slf4j:日志記錄、waLL: 防御sqL注入
      filters: stat,wall,slf4j
      # 要啟用PSCache,必須配置大于0,當(dāng)大于0時,poolPreparedStatements自動觸發(fā)修改為true。在Druid中,不會存在Oracle下PSCache占用內(nèi)存過多的問題,可以把這個數(shù)值配置大一些,比如說100
      max-pool-prepared-statement-per-connection-size: -1
      # 合并多個DruidDataSource的監(jiān)控數(shù)據(jù)
      use-global-data-source-stat: true
      # 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
      connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      web-stat-filter:
        # 是否啟用StatFilter默認(rèn)值true
        enabled: true
        # 添加過濾規(guī)則
        url-pattern: /*
        # 忽略過濾的格式
        exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico

      stat-view-servlet:
        # 是否啟用StatViewServlet默認(rèn)值true
        enabled: true
        # 訪問路徑為/druid時,跳轉(zhuǎn)到StatViewServlet
        url-pattern: /druid/*
        # 是否能夠重置數(shù)據(jù)
        reset-enable: false
        # 需要賬號密碼才能訪問控制臺,默認(rèn)為root
        login-username: root
        login-password: 123456
        # IP白名單
        allow: 127.0.0.1
        # IP黑名單(共同存在時,deny優(yōu)先于allow)
        deny:

配置好之后 Druid 會通過 DruidDataSourceAutoConfigure 自動裝配

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

@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);

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

3.?Druid 配置多數(shù)據(jù)源

屬性配置

# 數(shù)據(jù)源配置
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        druid:
            # 主庫數(shù)據(jù)源
            master:
                url: jdbc:mysql://localhost:3306/large_screen_monitor?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: root
            # 從庫數(shù)據(jù)源
            slave:
                # 從數(shù)據(jù)源開關(guān)/默認(rèn)關(guān)閉
                enabled: false
                url: 
                username: 
                password: 
            # 初始連接數(shù)
            initialSize: 5
            # 最小連接池數(shù)量
            minIdle: 10
            # 最大連接池數(shù)量
            maxActive: 20
            # 配置獲取連接等待超時的時間
            maxWait: 60000
            # 配置連接超時時間
            connectTimeout: 30000
            # 配置網(wǎng)絡(luò)超時時間
            socketTimeout: 60000
            # 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
            timeBetweenEvictionRunsMillis: 60000
            # 配置一個連接在池中最小生存的時間,單位是毫秒
            minEvictableIdleTimeMillis: 300000
            # 配置一個連接在池中最大生存的時間,單位是毫秒
            maxEvictableIdleTimeMillis: 900000
            # 配置檢測連接是否有效
            validationQuery: SELECT 1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter: 
                enabled: true
            statViewServlet:
                enabled: true
                # 設(shè)置白名單,不填則允許所有訪問
                allow:
                url-pattern: /druid/*
                # 控制臺管理用戶名和密碼
                login-username: webserver
                login-password: 123456
            filter:
                stat:
                    enabled: true
                    # 慢SQL記錄
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true
package com.test.framework.config.properties;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;

/**
 * druid 配置屬性
 */
@Configuration
public class DruidProperties
{
    @Value("${spring.datasource.druid.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.druid.minIdle}")
    private int minIdle;

    @Value("${spring.datasource.druid.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.druid.maxWait}")
    private int maxWait;

    @Value("${spring.datasource.druid.connectTimeout}")
    private int connectTimeout;

    @Value("${spring.datasource.druid.socketTimeout}")
    private int socketTimeout;

    @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
    private int maxEvictableIdleTimeMillis;

    @Value("${spring.datasource.druid.validationQuery}")
    private String validationQuery;

    @Value("${spring.datasource.druid.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.druid.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.druid.testOnReturn}")
    private boolean testOnReturn;

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

        /** 配置獲取連接等待超時的時間 */
        datasource.setMaxWait(maxWait);
        
        /** 配置驅(qū)動連接超時時間,檢測數(shù)據(jù)庫建立連接的超時時間,單位是毫秒 */
        datasource.setConnectTimeout(connectTimeout);
        
        /** 配置網(wǎng)絡(luò)超時時間,等待數(shù)據(jù)庫操作完成的網(wǎng)絡(luò)超時時間,單位是毫秒 */
        datasource.setSocketTimeout(socketTimeout);

        /** 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒 */
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

        /** 配置一個連接在池中最小、最大生存的時間,單位是毫秒 */
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);

        /**
         * 用來檢測連接是否有效的sql,要求是一個查詢語句,常用select 'x'。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會起作用。
         */
        datasource.setValidationQuery(validationQuery);
        /** 建議配置為true,不影響性能,并且保證安全性。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測連接是否有效。 */
        datasource.setTestWhileIdle(testWhileIdle);
        /** 申請連接時執(zhí)行validationQuery檢測連接是否有效,做了這個配置會降低性能。 */
        datasource.setTestOnBorrow(testOnBorrow);
        /** 歸還連接時執(zhí)行validationQuery檢測連接是否有效,做了這個配置會降低性能。 */
        datasource.setTestOnReturn(testOnReturn);
        return datasource;
    }
}

數(shù)據(jù)源枚舉

package com.test.common.enums;

/**
 * 數(shù)據(jù)源
 */
public enum DataSourceType
{
    /**
     * 主庫
     */
    MASTER,

    /**
     * 從庫
     */
    SLAVE
}

動態(tài)數(shù)據(jù)源

繼承 AbstractRoutingDataSource 就可以實(shí)現(xiàn)動態(tài)數(shù)據(jù)源了

實(shí)現(xiàn)了一個動態(tài)數(shù)據(jù)源類的構(gòu)造方法,主要是為了設(shè)置默認(rèn)數(shù)據(jù)源,以及以Map保存的各種目標(biāo)數(shù)據(jù)源。其中Map的key是設(shè)置的數(shù)據(jù)源名稱,value則是對應(yīng)的數(shù)據(jù)源(DataSource)。

在使用 DynamicDataSource 時,會調(diào)用determineCurrentLookupKey?方法拿到具體數(shù)據(jù)源的key,然后使用對應(yīng)的數(shù)據(jù)源;如果沒有對應(yīng)的數(shù)據(jù)源,會使用 DynamicDataSource 設(shè)置的默認(rèn)數(shù)據(jù)源

package com.test.framework.datasource;

import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 動態(tài)數(shù)據(jù)源
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

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

數(shù)據(jù)源切換

package com.test.framework.datasource;

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

/**
 * 數(shù)據(jù)源切換處理
 */
public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal維護(hù)變量,ThreadLocal為每個使用該變量的線程提供獨(dú)立的變量副本,
     *  所以每一個線程都可以獨(dú)立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 設(shè)置數(shù)據(jù)源的變量
     */
    public static void setDataSourceType(String dsType)
    {
        log.info("切換到{}數(shù)據(jù)源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 獲得數(shù)據(jù)源的變量
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空數(shù)據(jù)源變量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

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

通過配置類,將配置文件中的配置的數(shù)據(jù)庫信息轉(zhuǎn)換成datasource,并添加到DynamicDataSource中,同時通過@Bean將DynamicDataSource注入Spring中進(jìn)行管理,后期在進(jìn)行動態(tài)數(shù)據(jù)源添加時,會用到。

package com.test.framework.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.test.common.enums.DataSourceType;
import com.test.common.utils.spring.SpringUtils;
import com.test.framework.config.properties.DruidProperties;
import com.test.framework.datasource.DynamicDataSource;

/**
 * druid 配置多數(shù)據(jù)源
 */
@Configuration
public class DruidConfig
{
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }
    
    /**
     * 設(shè)置數(shù)據(jù)源
     * 
     * @param targetDataSources 備選數(shù)據(jù)源集合
     * @param sourceName 數(shù)據(jù)源名稱
     * @param beanName bean名稱
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
        try
        {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        }
        catch (Exception e)
        {
        }
    }

    /**
     * 去除監(jiān)控頁面底部的廣告
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
    {
        // 獲取web監(jiān)控頁面的參數(shù)
        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
        // 提取common.js的配置路徑
        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
        final String filePath = "support/http/resources/js/common.js";
        // 創(chuàng)建filter進(jìn)行過濾
        Filter filter = new Filter()
        {
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
            {
            }
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException
            {
                chain.doFilter(request, response);
                // 重置緩沖區(qū),響應(yīng)頭不會被重置
                response.resetBuffer();
                // 獲取common.js
                String text = Utils.readFromResource(filePath);
                // 正則替換banner, 除去底部的廣告信息
                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
                text = text.replaceAll("powered.*?shrek.wang</a>", "");
                response.getWriter().write(text);
            }
            @Override
            public void destroy()
            {
            }
        };
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns(commonJsPattern);
        return registrationBean;
    }
}

SpringBoot Bean工具類

package com.test.common.utils.spring;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * spring工具類 方便在非spring管理環(huán)境中獲取bean
 * 
 * @author webserver
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware 
{
    /** Spring應(yīng)用上下文環(huán)境 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
    {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
    {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 獲取對象
     *
     * @param name
     * @return Object 一個以所給名字注冊的bean的實(shí)例
     * @throws org.springframework.beans.BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 獲取類型為requiredType的對象
     *
     * @param clz
     * @return
     * @throws org.springframework.beans.BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一個與所給名稱匹配的bean定義,則返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }

    /**
     * 判斷以給定名字注冊的bean定義是一個singleton還是一個prototype。 如果與給定名字相應(yīng)的bean定義沒有被找到,將會拋出一個異常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注冊對象的類型
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果給定的bean名字在bean定義中有別名,則返回這些別名
     *
     * @param name
     * @return
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 獲取aop代理對象
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }

    /**
     * 獲取當(dāng)前的環(huán)境配置,無配置返回null
     *
     * @return 當(dāng)前的環(huán)境配置
     */
    public static String[] getActiveProfiles()
    {
        return applicationContext.getEnvironment().getActiveProfiles();
    }

    /**
     * 獲取當(dāng)前的環(huán)境配置,當(dāng)有多個環(huán)境配置時,只獲取第一個
     *
     * @return 當(dāng)前的環(huán)境配置
     */
    public static String getActiveProfile()
    {
        final String[] activeProfiles = getActiveProfiles();
        return (activeProfiles != null && activeProfiles.lenth > 0) ? activeProfiles[0] : null;
    }

    /**
     * 獲取配置文件中的值
     *
     * @param key 配置文件的key
     * @return 當(dāng)前的配置文件的值
     *
     */
    public static String getRequiredProperty(String key)
    {
        return applicationContext.getEnvironment().getRequiredProperty(key);
    }
}

手動切換數(shù)據(jù)源

在需要切換數(shù)據(jù)源的方法中使用DynamicDataSourceContextHolder類實(shí)現(xiàn)手動切換

public List<SysUser> selectUserList(SysUser user)
{
	DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
	List<SysUser> userList = userMapper.selectUserList(user);
	DynamicDataSourceContextHolder.clearDataSourceType();
	return userList;
}

4.實(shí)現(xiàn)注解切換數(shù)據(jù)源

自定義注解

package com.test.common.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.test.common.enums.DataSourceType;

/**
 * 自定義多數(shù)據(jù)源切換注解
 *
 * 優(yōu)先級:先方法,后類,如果方法覆蓋了類上的數(shù)據(jù)源類型,以方法的為準(zhǔn),否則以類上的為準(zhǔn)
 *
 * @author webserver
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切換數(shù)據(jù)源名稱
     */
    public DataSourceType value() default DataSourceType.MASTER;
}

根據(jù)注解實(shí)現(xiàn)aop切點(diǎn)?

代碼使用了@Around,通過ProceedingJoinPoint獲取注解信息,拿到注解傳遞值,然后設(shè)置當(dāng)前線程的數(shù)據(jù)源。

import java.util.Objects;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.test.common.annotation.DataSource;
import com.test.framework.datasource.DynamicDataSourceContextHolder;

/**
 * 多數(shù)據(jù)源處理
 * 
 * @author webserver
 */
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(com.example.common.annotation.DataSource)"
            + "|| @within(com.example.common.annotation.DataSource)")
    public void dsPointCut()
    {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        DataSource dataSource = getDataSource(point);

        if (dataSource != null)
        {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }

        try
        {
            return point.proceed();
        }
        finally
        {
            // 銷毀數(shù)據(jù)源 在執(zhí)行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * 獲取需要切換的數(shù)據(jù)源
     */
    public DataSource getDataSource(ProceedingJoinPoint point)
    {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource))
        {
            return dataSource;
        }

        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}

使用注解切換數(shù)據(jù)源

在需要使用多數(shù)據(jù)源方法或類上添加@DataSource注解,其中value用來表示數(shù)據(jù)源文章來源地址http://www.zghlxwxcb.cn/news/detail-842356.html

@DataSource(value = DataSourceType.SLAVE)
public List<SysUser> selectUserList(SysUser user)
{
	return userMapper.selectUserList(user);
}

5. 動態(tài)添加數(shù)據(jù)源

數(shù)據(jù)源實(shí)體類

@Data
@Accessors(chain = true)
public class DataSourceEntity {

    /**
     * 數(shù)據(jù)庫地址
     */
    private String url;
    /**
     * 數(shù)據(jù)庫用戶名
     */
    private String userName;
    /**
     * 密碼
     */
    private String passWord;
    /**
     * 數(shù)據(jù)庫驅(qū)動
     */
    private String driverClassName;
    /**
     * 數(shù)據(jù)庫key,即保存DynamicDataSource中Map的key
     */
    private String key;
}

修改DynamicDataSource

@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {

    private final Map<Object,Object> targetDataSourceMap;

    public DynamicDataSource(DataSource defaultDataSource,Map<Object, Object> targetDataSources){
        super.setDefaultTargetDataSource(defaultDataSource);
        super.setTargetDataSources(targetDataSources);
        this.targetDataSourceMap = targetDataSources;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }

    /**
     * 添加數(shù)據(jù)源信息
     * @param dataSources 數(shù)據(jù)源實(shí)體集合
     * @return 返回添加結(jié)果
     */
    public void createDataSource(List<DataSourceEntity> dataSources){
        try {
            if (CollectionUtils.isNotEmpty(dataSources)){
                for (DataSourceEntity ds : dataSources) {
                    //校驗(yàn)數(shù)據(jù)庫是否可以連接
                    Class.forName(ds.getDriverClassName());
                    DriverManager.getConnection(ds.getUrl(),ds.getUserName(),ds.getPassWord());
                    //定義數(shù)據(jù)源
                    DruidDataSource dataSource = new DruidDataSource();
                    BeanUtils.copyProperties(ds,dataSource);
                    //申請連接時執(zhí)行validationQuery檢測連接是否有效,這里建議配置為TRUE,防止取到的連接不可用
                    dataSource.setTestOnBorrow(true);
                    //建議配置為true,不影響性能,并且保證安全性。
                    //申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測連接是否有效。
                    dataSource.setTestWhileIdle(true);
                    //用來檢測連接是否有效的sql,要求是一個查詢語句。
                    dataSource.setValidationQuery("select 1 ");
                    dataSource.init();
                    this.targetDataSourceMap.put(ds.getKey(),dataSource);
                }
                super.setTargetDataSources(this.targetDataSourceMap);
                // 將TargetDataSources中的連接信息放入resolvedDataSources管理
                super.afterPropertiesSet();
                return Boolean.TRUE;
            }
        }catch (ClassNotFoundException | SQLException e) {
            log.error("---程序報錯---:{}", e.getMessage());
        }
        return Boolean.FALSE;
    }

    /**
     * 校驗(yàn)數(shù)據(jù)源是否存在
     * @param key 數(shù)據(jù)源保存的key
     * @return 返回結(jié)果,true:存在,false:不存在
     */
    public boolean existsDataSource(String key){
        return Objects.nonNull(this.targetDataSourceMap.get(key));
    }
}

存儲數(shù)據(jù)源信息的表

create table test_db_info(
    id int auto_increment primary key not null comment '主鍵Id',
    url varchar(255) not null comment '數(shù)據(jù)庫URL',
    username varchar(255) not null comment '用戶名',
    password varchar(255) not null comment '密碼',
    driver_class_name varchar(255) not null comment '數(shù)據(jù)庫驅(qū)動'
    name varchar(255) not null comment '數(shù)據(jù)庫名稱'
)

insert into test_db_info(url, username, password,driver_class_name, name)
value ('jdbc:mysql://xxxxx:3306/test2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false',
       'root','123456','com.mysql.cj.jdbc.Driver','add_slave')

SpringBoot啟動時加載數(shù)據(jù)源信息

@Component
public class LoadDataSourceRunner implements CommandLineRunner {
    @Resource
    private DynamicDataSource dynamicDataSource;
    @Resource
    private TestDbInfoMapper testDbInfoMapper;
    @Override
    public void run(String... args) throws Exception {
        List<TestDbInfo> testDbInfos = testDbInfoMapper.selectList(null);
        if (CollectionUtils.isNotEmpty(testDbInfos)) {
            List<DataSourceEntity> ds = new ArrayList<>();
            for (TestDbInfo testDbInfo : testDbInfos) {
                DataSourceEntity sourceEntity = new DataSourceEntity();
                BeanUtils.copyProperties(testDbInfo,sourceEntity);
                sourceEntity.setKey(testDbInfo.getName());
                ds.add(sourceEntity);
            }
            dynamicDataSource.createDataSource(ds);
        }
    }
}

到了這里,關(guān)于SpringBoot整合Druid數(shù)據(jù)庫連接池&多數(shù)據(jù)源&注解切換&動態(tài)添加的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【精·超詳細(xì)】SpringBoot 配置多個數(shù)據(jù)源(連接多個數(shù)據(jù)庫)

    【精·超詳細(xì)】SpringBoot 配置多個數(shù)據(jù)源(連接多個數(shù)據(jù)庫)

    目錄 1.項(xiàng)目路徑 2.pom.xml? 引入依賴: 3.application.yml配置文件: 4.兩個entity類 5.Conroller 6.兩個Service以及兩個ServiceImpl? 7.兩個Mapper及兩個Mapper.xml? 8.運(yùn)行Application? 然后在瀏覽器請求 9.查看兩個數(shù)據(jù)庫是否有新增數(shù)據(jù) ? ? ? ? ? 總結(jié): 1.pom.xml 引入依賴: dynamic-datasource-spring-b

    2024年02月12日
    瀏覽(41)
  • SpringBoot -02 SpringBoot整合Mybatis、Druid數(shù)據(jù)源、單元測試、JSP

    mybatis起步依賴 mysql啟動依賴 數(shù)據(jù)源配置 mybatis相關(guān)配置 加載mybatis配置文件 mybatis別名配置 日志配置 加載mapper映射文件配置文件 http://localhost:8080/users Druid連接池后臺監(jiān)控:http://localhost:8080/druid/index.html SpringBoot對JSP的支持不是很友好,所以JSP很少被使用 springboot 不支持jsp 但是

    2024年02月06日
    瀏覽(54)
  • SpringBoot整合Mybatis-Plus、Druid配置多數(shù)據(jù)源

    SpringBoot整合Mybatis-Plus、Druid配置多數(shù)據(jù)源

    目錄 1.初始化項(xiàng)目 1.1.初始化工程 1.2.添加依賴 1.3.配置yml文件 1.4.Spring Boot 啟動類中添加?@MapperScan?注解,掃描 Mapper 文件夾 1.5.配置使用數(shù)據(jù)源 1.5.1.注解方式 1.5.2.基于AOP手動實(shí)現(xiàn)多數(shù)據(jù)源原生的方式 2.結(jié)果展示 Mybatis-Plus:簡介 | MyBatis-Plus (baomidou.com) 在正式開始之前,先初始

    2024年02月11日
    瀏覽(24)
  • SpringBoot整合Mybatis-Plus+Druid實(shí)現(xiàn)多數(shù)據(jù)源

    SpringBoot整合Mybatis-Plus+Druid實(shí)現(xiàn)多數(shù)據(jù)源

    ??本文主要講解 springboot +mybatisplus + druid 實(shí)現(xiàn)多數(shù)據(jù)源配置功能 ?? 主頁傳送門:?? 傳送 Spring Boot: ? ?Spring Boot是一個基于Spring框架的開源Java開發(fā)框架,旨在簡化Spring應(yīng)用程序的開發(fā)、配置和部署。它提供了一種快速、敏捷的方式來構(gòu)建獨(dú)立的、生產(chǎn)級別的Spring應(yīng)用程

    2024年02月09日
    瀏覽(25)
  • 【SpringBoot 3.x】整合Mybatis-Plus多數(shù)據(jù)源、Druid

    【SpringBoot 3.x】整合Mybatis-Plus多數(shù)據(jù)源、Druid

    開發(fā)依賴 版本 Spring Boot 3.0.6 Mybatis-Plus 3.5.3.1 dynamic-datasource-spring-boot-starter 3.6.1 JDK 20 SpringBoot啟動類修改 由于排除了DruidDataSourceAutoConfigure類的自動裝載,就需要手工指定裝配以下幾個類 查看DruidDataSourceAutoConfigure這個類的源碼可以看出,需要把@Import帶進(jìn)來的幾個類進(jìn)行自動裝

    2024年02月04日
    瀏覽(54)
  • SpringBoot 整合 Neo4j、MySQL 多數(shù)據(jù)源方案(Druid Mybatis DynamicDatasource)

    本文總結(jié)了Neo4j和Spring/SpringBoot、Alibaba Druid、Dynamic Datasource、Mybatis等整合方案,對相應(yīng)配置做了詳細(xì)說明。 添加Neo4j JDBC Driver依賴 添加application.yml配置 添加Neo4j JDBC Driver + Alibaba Druid依賴 添加application.yml配置 添加Neo4j JDBC Driver、Alibaba Druid、Dynamic DataSource依賴 添加application.y

    2023年04月08日
    瀏覽(17)
  • Springboot整合Durid-數(shù)據(jù)庫連接池

    一、Springboot使用Durid快速開始 1、 添加依賴 2、添加配置項(xiàng) (1) durid連接池配置

    2024年02月11日
    瀏覽(23)
  • 數(shù)據(jù)庫連接池Druid

    數(shù)據(jù)庫連接池Druid

    在 Spring Boot 項(xiàng)目中,數(shù)據(jù)庫連接池已經(jīng)成為標(biāo)配,然而,我曾經(jīng)遇到過不少連接池異常導(dǎo)致業(yè)務(wù)錯誤的事故。很多經(jīng)驗(yàn)豐富的工程師也可能不小心在這方面出現(xiàn)問題。 在這篇文章中,我們將探討數(shù)據(jù)庫連接池,深入解析其實(shí)現(xiàn)機(jī)制,以便更好地理解和規(guī)避潛在的風(fēng)險。 假如

    2024年02月05日
    瀏覽(25)
  • 【Java】數(shù)據(jù)庫連接池--Druid

    【Java】數(shù)據(jù)庫連接池--Druid

    數(shù)據(jù)庫連接池是一個容器,負(fù)責(zé)分配、管理數(shù)據(jù)庫連接(Connection) 它允許應(yīng)用程序重復(fù)使用一個現(xiàn)有的數(shù)據(jù)庫連接,而不是再重新建立一個 釋放空閑時間超過最大空閑時間的數(shù)據(jù)庫連接來避免因?yàn)闆]有釋放數(shù)據(jù)庫連接而引起的數(shù)據(jù)庫連接遺漏 好處: 資源重用 提升系統(tǒng)的響應(yīng)

    2024年01月22日
    瀏覽(51)
  • Druid連接池實(shí)現(xiàn)數(shù)據(jù)庫加密

    Druid連接池實(shí)現(xiàn)數(shù)據(jù)庫加密

    不難發(fā)現(xiàn),以我們現(xiàn)在的開發(fā)習(xí)慣,無論是公司的項(xiàng)目還是個人的項(xiàng)目,都會選擇將源碼上傳到 Git 服務(wù)器(GitHub、Gitee 或是自建服務(wù)器),但只要將源碼提交到公網(wǎng)服務(wù)器就會存在源碼泄漏的風(fēng)險,而數(shù)據(jù)庫配置信息作為源碼的一部分,一旦出現(xiàn)源碼泄漏,那么數(shù)據(jù)庫中的

    2024年02月13日
    瀏覽(30)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包