摘要:springboot1.x到springboot2.0配置變化有一點變化,網上關於springboot2.0配置多數據源的資料也比較少,爲了讓你們配置多數據源從springboot1.x升級到springboot2.0少踩寫坑,博主在此介紹用springboot2.0來進行動態數據源切換。(在博客的最後會給你們提供源碼的下載地址)java
1、引入依賴mysql
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.heshouyou</groupId> <artifactId>dynamic-datasource</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>dynamic-datasource</name> <description>dynamic-datasource project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、編輯application.ymlgit
server.port=8080 #數據源配置(默認) #useSSL=false MySQL在高版本須要指明是否進行SSL鏈接(不加第一次鏈接數據庫會有警告信息) spring.datasource.driver=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=123456 #ds1,ds2 其餘兩個數據源 slave.datasource.names=ds1,ds2 #ds1 slave.datasource.ds1.driver=com.mysql.jdbc.Driver slave.datasource.ds1.url=jdbc:mysql://localhost:3306/test1?useSSL=false slave.datasource.ds1.username=root slave.datasource.ds1.password=123456 #ds2 slave.datasource.ds2.driver=com.mysql.jdbc.Driver slave.datasource.ds2.url=jdbc:mysql://localhost:3306/test2?useSSL=false slave.datasource.ds2.username=root slave.datasource.ds2.password=123456 #mapper.xml文件 mybatis.mapper-locations=classpath:mapper/*.xml #實體類包 mybatis.type-aliases-package=datasource.springboot_datasource.entity
3、DataSource工具類github
動態切換類web
package com.chaoqi.springboot_datasource.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * @Author caiChaoqi * @Date 2018-06-23 * @Description 動態數據源 * AbstractRoutingDataSource(每執行一次數據庫,動態獲取DataSource) */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
動態數據源上下文管理
package com.chaoqi.springboot_datasource.config; import org.apache.log4j.Logger; import java.util.ArrayList; import java.util.List; /** * @Author caiChaoqi * @Date 2018-06-23 * @Description 動態數據源上下文管理 */ public class DynamicDataSourceContextHolder { private Logger logger = Logger.getLogger(DynamicDataSourceContextHolder.class); //存放當前線程使用的數據源類型信息 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); //存放數據源id public static List<String> dataSourceIds = new ArrayList<String>(); //設置數據源 public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } //獲取數據源 public static String getDataSourceType() { return contextHolder.get(); } //清除數據源 public static void clearDataSourceType() { contextHolder.remove(); } //判斷當前數據源是否存在 public static boolean isContainsDataSource(String dataSourceId) { return dataSourceIds.contains(dataSourceId); } }
初始化數據源和提供了執行動態切換數據源的工具類
package com.chaoqi.springboot_datasource.config; import org.apache.log4j.Logger; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * @Author caiChaoqi * @Date 2018-06-23 * @Description 註冊動態數據源 * 初始化數據源和提供了執行動態切換數據源的工具類 * EnvironmentAware(獲取配置文件配置的屬性值) */ public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private Logger logger = Logger.getLogger(DynamicDataSourceRegister.class); //指定默認數據源(springboot2.0默認數據源是hikari如何想使用其餘數據源能夠本身配置) private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource"; //默認數據源 private DataSource defaultDataSource; //用戶自定義數據源 private Map<String, DataSource> slaveDataSources = new HashMap<>(); @Override public void setEnvironment(Environment environment) { initDefaultDataSource(environment); initslaveDataSources(environment); } private void initDefaultDataSource(Environment env) { // 讀取主數據源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("spring.datasource.driver")); dsMap.put("url", env.getProperty("spring.datasource.url")); dsMap.put("username", env.getProperty("spring.datasource.username")); dsMap.put("password", env.getProperty("spring.datasource.password")); defaultDataSource = buildDataSource(dsMap); } private void initslaveDataSources(Environment env) { // 讀取配置文件獲取更多數據源 String dsPrefixs = env.getProperty("slave.datasource.names"); for (String dsPrefix : dsPrefixs.split(",")) { // 多個數據源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver")); dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url")); dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username")); dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password")); DataSource ds = buildDataSource(dsMap); slaveDataSources.put(dsPrefix, ds); } } @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); //添加默認數據源 targetDataSources.put("dataSource", this.defaultDataSource); DynamicDataSourceContextHolder.dataSourceIds.add("dataSource"); //添加其餘數據源 targetDataSources.putAll(slaveDataSources); for (String key : slaveDataSources.keySet()) { DynamicDataSourceContextHolder.dataSourceIds.add(key); } //建立DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); mpv.addPropertyValue("targetDataSources", targetDataSources); //註冊 - BeanDefinitionRegistry beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition); logger.info("Dynamic DataSource Registry"); } public DataSource buildDataSource(Map<String, Object> dataSourceMap) { try { Object type = dataSourceMap.get("type"); if (type == null) { type = DATASOURCE_TYPE_DEFAULT;// 默認DataSource } Class<? extends DataSource> dataSourceType; dataSourceType = (Class<? extends DataSource>) Class.forName((String) type); String driverClassName = dataSourceMap.get("driver").toString(); String url = dataSourceMap.get("url").toString(); String username = dataSourceMap.get("username").toString(); String password = dataSourceMap.get("password").toString(); // 自定義DataSource配置 DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url) .username(username).password(password).type(dataSourceType); return factory.build(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }
自定義註解spring
package com.chaoqi.springboot_datasource.config; import java.lang.annotation.*; /** * @Author caiChaoqi * @Date 2018-06-23 * @Description 做用於類、接口或者方法上 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String name(); }
動態數據源通知
package com.chaoqi.springboot_datasource.config; import org.apache.log4j.Logger; 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.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @Author caiChaoqi * @Description 動態數據源通知 * @Date 2018-06-23 */ @Aspect @Order(-1)//保證在@Transactional以前執行 @Component public class DynamicDattaSourceAspect { private Logger logger = Logger.getLogger(DynamicDattaSourceAspect.class); //改變數據源 @Before("@annotation(targetDataSource)") public void changeDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) { String dbid = targetDataSource.name(); if (!DynamicDataSourceContextHolder.isContainsDataSource(dbid)) { //joinPoint.getSignature() :獲取鏈接點的方法簽名對象 logger.error("數據源 " + dbid + " 不存在使用默認的數據源 -> " + joinPoint.getSignature()); } else { logger.debug("使用數據源:" + dbid); DynamicDataSourceContextHolder.setDataSourceType(dbid); } } @After("@annotation(targetDataSource)") public void clearDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) { logger.debug("清除數據源 " + targetDataSource.name() + " !"); DynamicDataSourceContextHolder.clearDataSourceType(); } }
核心代碼以所有展現,源碼下載地址:https://github.com/caicahoqi/ChaoqiIsPrivateLibrary 若是在項目搭建中遇到問題能夠在評論區留言,博主看到第一時間會給予回覆,謝謝sql