springboot配置druid多數(shù)據(jù)源的示例代碼
1、配置多數(shù)據(jù)源所需要的jar
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.10</version> </dependency> <!-- 配置注解執(zhí)行器配置完成后,當(dāng)執(zhí)行類中已經(jīng)定義了對象和該對象的字段后,在配置文件中對該類賦值時,便會非常方便的彈出提示信息 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 阿里數(shù)據(jù)庫連接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.11</version> </dependency>
2、配置多數(shù)據(jù)源所需要的工具類
SpringUtils
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 wz */ @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的實例 * @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 StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; } /** * 獲取配置文件中的值 * * @param key 配置文件的key * @return 當(dāng)前的配置文件的值 */ public static String getRequiredProperty(String key) { return applicationContext.getEnvironment().getRequiredProperty(key); } }
StringUtils
import cn.hutool.core.text.StrFormatter; import org.springframework.util.AntPathMatcher; import java.util.*; /** * 字符串工具類 * * @author wz */ public class StringUtils extends org.apache.commons.lang3.StringUtils { /** * 空字符串 */ private static final String NULLSTR = ""; /** * 下劃線 */ private static final char SEPARATOR = '_'; /** * 獲取參數(shù)不為空值 * * @param value defaultValue 要判斷的value * @return value 返回值 */ public static <T> T nvl(T value, T defaultValue) { return value != null ? value : defaultValue; } /** * * 判斷一個Collection是否為空, 包含List,Set,Queue * * @param coll 要判斷的Collection * @return true:為空 false:非空 */ public static boolean isEmpty(Collection<?> coll) { return isNull(coll) || coll.isEmpty(); } /** * * 判斷一個Collection是否非空,包含List,Set,Queue * * @param coll 要判斷的Collection * @return true:非空 false:空 */ public static boolean isNotEmpty(Collection<?> coll) { return !isEmpty(coll); } /** * * 判斷一個對象數(shù)組是否為空 * * @param objects 要判斷的對象數(shù)組 * * @return true:為空 false:非空 */ public static boolean isEmpty(Object[] objects) { return isNull(objects) || (objects.length == 0); } /** * * 判斷一個對象數(shù)組是否非空 * * @param objects 要判斷的對象數(shù)組 * @return true:非空 false:空 */ public static boolean isNotEmpty(Object[] objects) { return !isEmpty(objects); } /** * * 判斷一個Map是否為空 * * @param map 要判斷的Map * @return true:為空 false:非空 */ public static boolean isEmpty(Map<?, ?> map) { return isNull(map) || map.isEmpty(); } /** * * 判斷一個Map是否為空 * * @param map 要判斷的Map * @return true:非空 false:空 */ public static boolean isNotEmpty(Map<?, ?> map) { return !isEmpty(map); } /** * * 判斷一個字符串是否為空串 * * @param str String * @return true:為空 false:非空 */ public static boolean isEmpty(String str) { return isNull(str) || NULLSTR.equals(str.trim()); } /** * * 判斷一個字符串是否為非空串 * * @param str String * @return true:非空串 false:空串 */ public static boolean isNotEmpty(String str) { return !isEmpty(str); } /** * * 判斷一個對象是否為空 * * @param object Object * @return true:為空 false:非空 */ public static boolean isNull(Object object) { return object == null; } /** * * 判斷一個對象是否非空 * * @param object Object * @return true:非空 false:空 */ public static boolean isNotNull(Object object) { return !isNull(object); } /** * * 判斷一個對象是否是數(shù)組類型(Java基本型別的數(shù)組) * * @param object 對象 * @return true:是數(shù)組 false:不是數(shù)組 */ public static boolean isArray(Object object) { return isNotNull(object) && object.getClass().isArray(); } /** * 去空格 */ public static String trim(String str) { return (str == null ? "" : str.trim()); } /** * 截取字符串 * * @param str 字符串 * @param start 開始 * @return 結(jié)果 */ public static String substring(final String str, int start) { if (str == null) { return NULLSTR; } if (start < 0) { start = str.length() + start; } if (start < 0) { start = 0; } if (start > str.length()) { return NULLSTR; } return str.substring(start); } /** * 截取字符串 * * @param str 字符串 * @param start 開始 * @param end 結(jié)束 * @return 結(jié)果 */ public static String substring(final String str, int start, int end) { if (str == null) { return NULLSTR; } if (end < 0) { end = str.length() + end; } if (start < 0) { start = str.length() + start; } if (end > str.length()) { end = str.length(); } if (start > end) { return NULLSTR; } if (start < 0) { start = 0; } if (end < 0) { end = 0; } return str.substring(start, end); } /** * 格式化文本, {} 表示占位符<br> * 此方法只是簡單將占位符 {} 按照順序替換為參數(shù)<br> * 如果想輸出 {} 使用 \\轉(zhuǎn)義 { 即可,如果想輸出 {} 之前的 \ 使用雙轉(zhuǎn)義符 \\\\ 即可<br> * 例:<br> * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> * 轉(zhuǎn)義{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> * 轉(zhuǎn)義\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> * * @param template 文本模板,被替換的部分用 {} 表示 * @param params 參數(shù)值 * @return 格式化后的文本 */ public static String format(String template, Object... params) { if (isEmpty(params) || isEmpty(template)) { return template; } return StrFormatter.format(template, params); } /** * 字符串轉(zhuǎn)set * * @param str 字符串 * @param sep 分隔符 * @return set集合 */ public static final Set<String> str2Set(String str, String sep) { return new HashSet<String>(str2List(str, sep, true, false)); } /** * 字符串轉(zhuǎn)list * * @param str 字符串 * @param sep 分隔符 * @param filterBlank 過濾純空白 * @param trim 去掉首尾空白 * @return list集合 */ public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) { List<String> list = new ArrayList<String>(); if (StringUtils.isEmpty(str)) { return list; } // 過濾空白字符串 if (filterBlank && StringUtils.isBlank(str)) { return list; } String[] split = str.split(sep); for (String string : split) { if (filterBlank && StringUtils.isBlank(string)) { continue; } if (trim) { string = string.trim(); } list.add(string); } return list; } /** * 查找指定字符串是否包含指定字符串列表中的任意一個字符串同時串忽略大小寫 * * @param cs 指定字符串 * @param searchCharSequences 需要檢查的字符串?dāng)?shù)組 * @return 是否包含任意一個字符串 */ public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) { if (isEmpty(cs) || isEmpty(searchCharSequences)) { return false; } for (CharSequence testStr : searchCharSequences) { if (containsIgnoreCase(cs, testStr)) { return true; } } return false; } /** * 駝峰轉(zhuǎn)下劃線命名 */ public static String toUnderScoreCase(String str) { if (str == null) { return null; } StringBuilder sb = new StringBuilder(); // 前置字符是否大寫 boolean preCharIsUpperCase = true; // 當(dāng)前字符是否大寫 boolean curreCharIsUpperCase = true; // 下一字符是否大寫 boolean nexteCharIsUpperCase = true; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (i > 0) { preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); } else { preCharIsUpperCase = false; } curreCharIsUpperCase = Character.isUpperCase(c); if (i < (str.length() - 1)) { nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); } if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { sb.append(SEPARATOR); } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { sb.append(SEPARATOR); } sb.append(Character.toLowerCase(c)); } return sb.toString(); } /** * 是否包含字符串 * * @param str 驗證字符串 * @param strs 字符串組 * @return 包含返回true */ public static boolean inStringIgnoreCase(String str, String... strs) { if (str != null && strs != null) { for (String s : strs) { if (str.equalsIgnoreCase(trim(s))) { return true; } } } return false; } /** * 刪除最后一個字符串 * * @param str 輸入字符串 * @param spit 以什么類型結(jié)尾的 * @return 截取后的字符串 */ public static String lastStringDel(String str, String spit) { if (!StringUtils.isEmpty(str) && str.endsWith(spit)) { return str.subSequence(0, str.length() - 1).toString(); } return str; } /** * 將下劃線大寫方式命名的字符串轉(zhuǎn)換為駝峰式。如果轉(zhuǎn)換前的下劃線大寫方式命名的字符串為空,則返回空字符串。 例如:HELLO_WORLD->HelloWorld * * @param name 轉(zhuǎn)換前的下劃線大寫方式命名的字符串 * @return 轉(zhuǎn)換后的駝峰式命名的字符串 */ public static String convertToCamelCase(String name) { StringBuilder result = new StringBuilder(); // 快速檢查 if (name == null || name.isEmpty()) { // 沒必要轉(zhuǎn)換 return ""; } else if (!name.contains("_")) { // 不含下劃線,僅將首字母大寫 return name.substring(0, 1).toUpperCase() + name.substring(1); } // 用下劃線將原始字符串分割 String[] camels = name.split("_"); for (String camel : camels) { // 跳過原始字符串中開頭、結(jié)尾的下?lián)Q線或雙重下劃線 if (camel.isEmpty()) { continue; } // 首字母大寫 result.append(camel.substring(0, 1).toUpperCase()); result.append(camel.substring(1).toLowerCase()); } return result.toString(); } /** * 駝峰式命名法 * 例如:user_name->userName */ public static String toCamelCase(String s) { if (s == null) { return null; } if (s.indexOf(SEPARATOR) == -1) { return s; } s = s.toLowerCase(); StringBuilder sb = new StringBuilder(s.length()); boolean upperCase = false; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == SEPARATOR) { upperCase = true; } else if (upperCase) { sb.append(Character.toUpperCase(c)); upperCase = false; } else { sb.append(c); } } return sb.toString(); } /** * 查找指定字符串是否匹配指定字符串列表中的任意一個字符串 * * @param str 指定字符串 * @param strs 需要檢查的字符串?dāng)?shù)組 * @return 是否匹配 */ public static boolean matches(String str, List<String> strs) { if (isEmpty(str) || isEmpty(strs)) { return false; } for (String pattern : strs) { if (isMatch(pattern, str)) { return true; } } return false; } /** * 判斷url是否與規(guī)則配置: * ? 表示單個字符; * * 表示一層路徑內(nèi)的任意字符串,不可跨層級; * ** 表示任意層路徑; * * @param pattern 匹配規(guī)則 * @param url 需要匹配的url * @return */ public static boolean isMatch(String pattern, String url) { AntPathMatcher matcher = new AntPathMatcher(); return matcher.match(pattern, url); } @SuppressWarnings("unchecked") public static <T> T cast(Object obj) { return (T) obj; } /** * 數(shù)字左邊補齊0,使之達到指定長度。注意,如果數(shù)字轉(zhuǎn)換為字符串后,長度大于size,則只保留 最后size個字符。 * * @param num 數(shù)字對象 * @param size 字符串指定長度 * @return 返回數(shù)字的字符串格式,該字符串為指定長度。 */ public static final String padl(final Number num, final int size) { return padl(num.toString(), size, '0'); } /** * 字符串左補齊。如果原始字符串s長度大于size,則只保留最后size個字符。 * * @param s 原始字符串 * @param size 字符串指定長度 * @param c 用于補齊的字符 * @return 返回指定長度的字符串,由原字符串左補齊或截取得到。 */ public static final String padl(final String s, final int size, final char c) { final StringBuilder sb = new StringBuilder(size); if (s != null) { final int len = s.length(); if (s.length() <= size) { for (int i = size - len; i > 0; i--) { sb.append(c); } sb.append(s); } else { return s.substring(len - size, len); } } else { for (int i = size; i > 0; i--) { sb.append(c); } } return sb.toString(); } }
3、DataSourceType 枚舉類
/** * 數(shù)據(jù)源 * * @author wz */ public enum DataSourceType { /** * 主庫 */ MASTER, /** * 從庫 */ SLAVE }
4、DruidProperties druid 配置屬性
import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; /** * druid 配置屬性 * * @author wz */ @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.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); /** 配置間隔多久才進行一次檢測,檢測需要關(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; } }
5、DruidConfig druid 配置多數(shù)據(jù)源
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 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 sanss.ywyx.utils.SpringUtils; import javax.servlet.*; import javax.sql.DataSource; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * druid 配置多數(shù)據(jù)源 * * @author wz */ @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進行過濾 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; } }
6、DynamicDataSource 動態(tài)數(shù)據(jù)源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.Map; /** * 動態(tài)數(shù)據(jù)源 * * @author wz */ 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(); } }
7、DynamicDataSourceContextHolder 數(shù)據(jù)源切換處理
import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 數(shù)據(jù)源切換處理 * * @author wz */ public class DynamicDataSourceContextHolder { public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); /** * 使用ThreadLocal維護變量,ThreadLocal為每個使用該變量的線程提供獨立的變量副本, * 所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應(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(); } }
8、DataSource 自定義多數(shù)據(jù)源切換注解
import java.lang.annotation.*; /** * 自定義多數(shù)據(jù)源切換注解 * <p> * 優(yōu)先級:先方法,后類,如果方法覆蓋了類上的數(shù)據(jù)源類型,以方法的為準(zhǔn),否則以類上的為準(zhǔn) * * @author wz */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface DataSource { /** * 切換數(shù)據(jù)源名稱 */ public DataSourceType value() default DataSourceType.MASTER; }
9、DataSourceAspect 多數(shù)據(jù)源處理
package sanss.ywyx.config.druid; 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 sanss.ywyx.utils.StringUtils; /** * 多數(shù)據(jù)源處理 * * @author ruoyi */ @Aspect @Order(1) @Component public class DataSourceAspect { protected Logger logger = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(sanss.ywyx.config.druid.DataSource)" + "|| @within(sanss.ywyx.config.druid.DataSource)") public void dsPointCut() { } @Around("dsPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { DataSource dataSource = getDataSource(point); if (StringUtils.isNotNull(dataSource)) { 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); } }
10、yml配置
datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.postgresql.Driver druid: # 主庫數(shù)據(jù)源 master: url: username: password: # 從庫數(shù)據(jù)源 slave: # 從數(shù)據(jù)源開關(guān)/默認關(guān)閉 enabled: true url: username: password: # 初始連接數(shù) initialSize: 5 # 最小連接池數(shù)量 minIdle: 10 # 最大連接池數(shù)量 maxActive: 20 # 配置獲取連接等待超時的時間 maxWait: 60000 # 配置間隔多久才進行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一個連接在池中最小生存的時間,單位是毫秒 minEvictableIdleTimeMillis: 300000 # 配置一個連接在池中最大生存的時間,單位是毫秒 maxEvictableIdleTimeMillis: 900000 # 配置檢測連接是否有效 validationQuery: select version() testWhileIdle: true testOnBorrow: false testOnReturn: false webStatFilter: enabled: true statViewServlet: enabled: true # 設(shè)置白名單,不填則允許所有訪問 allow: url-pattern: /druid/* # 控制臺管理用戶名和密碼 login-username: admin 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
到此這篇關(guān)于springboot配置druid多數(shù)據(jù)源的示例代碼的文章就介紹到這了,更多相關(guān)springboot druid多數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot請求異常處理并返回對應(yīng)的html頁面
這篇文章主要介紹了spring boot處理請求異常并返回對應(yīng)的html頁面,包括404異常處理和500異常處理,需要的朋友可以參考下2017-07-07springBoot集成mybatis 轉(zhuǎn)換為 mybatis-plus方式
這篇文章主要介紹了springBoot集成mybatis 轉(zhuǎn)換為 mybatis-plus方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12idea pom導(dǎo)入net.sf.json的jar包失敗的解決方案
JSON(JavaScript Object Notation,JS對象簡譜)是一種輕量級的數(shù)據(jù)交換格式,這篇文章主要介紹了idea pom導(dǎo)入net.sf.json的jar包失敗的解決方案,感興趣的朋友一起看看吧2023-11-11詳解領(lǐng)域驅(qū)動設(shè)計之事件驅(qū)動與CQRS
這篇文章分析了如何應(yīng)用事件來分離軟件核心復(fù)雜度。探究CQRS為什么廣泛應(yīng)用于DDD項目中,以及如何落地實現(xiàn)CQRS框架。當(dāng)然我們也要警惕一些失敗的教訓(xùn),利弊分析以后再去抉擇正確的應(yīng)對之道2021-06-06關(guān)于controller的異常處理及service層的事務(wù)控制方式
這篇文章主要介紹了關(guān)于controller的異常處理及service層的事務(wù)控制方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02java語言基礎(chǔ)之標(biāo)識符和命名規(guī)則詳解
這篇文章主要給大家介紹了關(guān)于java語言基礎(chǔ)之標(biāo)識符和命名規(guī)則的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03