Springboot+aop實現(xiàn)配置多數(shù)據(jù)源的示例代碼
前言
? 這是我司在對于動態(tài)數(shù)據(jù)源的一個處理方法,采用動態(tài)加載數(shù)據(jù)庫信息實現(xiàn)動態(tài)數(shù)據(jù)源的切換。通過使用Spring AOP + 注解來替換當前線程ThreadLocal中的值,并且通過重寫AbstractRoutingDataSource類重寫determineCurrentLookUpKey()方法,實現(xiàn)動態(tài)數(shù)據(jù)源切換,滿足功能實現(xiàn)的代碼0侵入性,并且高度解耦,實現(xiàn)可拔插功能效果
1、添加依賴(本項目是一個springboot項目)
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-core</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.22</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.32</version> <scope>runtime</scope> </dependency>
2、定義數(shù)據(jù)庫類型枚舉類(enum)
/** * 數(shù)據(jù)庫配置名字字段 * @author zh * @data 17點14分 * */ public enum DBTypeEnum { cs1db("cs1-db"), cs2db("cs2-db"); private String value; DBTypeEnum(String value) { this.value = value; } public String getValue() { return value; } }
此枚舉主要是用于簡化if-else語句。
3、定義注解(annotation)
/** * @author zh * @data 17點18分 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DataSourceSwitch { /**** * 順便設(shè)置下默認數(shù)據(jù)源 * @return */ DBTypeEnum value() default DBTypeEnum.cs1db; }
定義DataSourceSwitch注解,默認數(shù)據(jù)源為cs1db(數(shù)據(jù)庫一)。
4、編寫ThreadLocal存儲數(shù)據(jù)上下文信息
/** * @author zh */ public class DbContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal<>(); /** * 設(shè)置數(shù)據(jù)源 * @param dbTypeEnum */ public static void setDbType(DBTypeEnum dbTypeEnum) { contextHolder.set(dbTypeEnum.getValue()); } /** * 取得當前數(shù)據(jù)源 * @return */ public static String getDbType() { return (String) contextHolder.get(); } /** * 清除上下文數(shù)據(jù) */ public static void clearDbType() { contextHolder.remove(); } }
工具類DbContextHolder用于存儲數(shù)據(jù)名上下文。
5、獲取數(shù)據(jù)源信息
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * @author zh */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * 取得當前使用哪個數(shù)據(jù)源 * @return */ @Override protected Object determineCurrentLookupKey() { return DbContextHolder.getDbType(); } }
工具類DynamicDataSource繼承AbstractRoutingDataSource重寫determineCurrentLookupKey()方法,從工具類DbContextHolder中獲取當前數(shù)據(jù)源信息。
6、自定義切面(Aspect)
/** * @author zh * 數(shù)據(jù)源AOP注解實現(xiàn) */ @Component @Aspect @Order(-100) public class DataSourceSwitchAspect { private Logger log= LoggerFactory.getLogger(DataSourceSwitchAspect.class); @Pointcut("execution(* com.zh.cn.business.cs1db..*.*(..))") private void cs1dbAspect() { } @Pointcut("execution(* com.zh.cn.business.cs2db..*.*(..))") private void cs2dbAspect() { } @Before( "cs1dbAspect()" ) public void basic(JoinPoint joinPoint) { //log.info("切換到cs1db 數(shù)據(jù)源..."); setDataSource(joinPoint, DBTypeEnum.cs1db); } @Before("cs2dbAspect()" ) public void order (JoinPoint joinPoint) { //log.info("切換到cs2db 數(shù)據(jù)源..."); setDataSource(joinPoint,DBTypeEnum.cs2db); } /** * 添加注解方式,如果有注解優(yōu)先注解,沒有則按傳過來的數(shù)據(jù)源配置 * @param joinPoint * @param dbTypeEnum */ private void setDataSource(JoinPoint joinPoint, DBTypeEnum dbTypeEnum) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); DataSourceSwitch dataSourceSwitch = methodSignature.getMethod().getAnnotation(DataSourceSwitch.class); if (Objects.isNull(dataSourceSwitch) || Objects.isNull(dataSourceSwitch.value())) { DbContextHolder.setDbType(dbTypeEnum); }else{ // log.info("根據(jù)注解來切換數(shù)據(jù)源,注解值為:"+dataSourceSwitch.value()); switch (dataSourceSwitch.value().getValue()) { case "cs1-db": DbContextHolder.setDbType(DBTypeEnum.cs1db); break; case "cs2-db": DbContextHolder.setDbType(DBTypeEnum.cs2db); break; default: DbContextHolder.setDbType(dbTypeEnum); } } } }
通過springAop自定義切面,切入點在* com.zh.cn.business.cs2db..*.*(..))
,表示切入點在com.zh.cn.business.cs2db包下的任意子包、任意方法和任意返回值,并通過setDataSource()方法設(shè)置工具類DbContextHolder中數(shù)據(jù)源信息。
7、配置數(shù)據(jù)源信息
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.generator.config.rules.DbType; import com.zh.cn.Utils.DynamicDataSource; import com.zh.cn.constants.DBTypeEnum; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 加載DataSource */ @Configuration @MapperScan({"com.zh.cn.**.mapper"}) public class MybatisPlusConfig { /** * mapper-plus分頁插件<br> * 文檔:http://mp.baomidou.com<br> */ /* @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); //paginationInterceptor.setLocalPage(true);// 開啟 PageHelper 的支持 return paginationInterceptor; }*/ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //樂觀鎖 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } @Bean(name = "cs1db") @ConfigurationProperties(prefix = "spring.datasource.druid.cs1-db" ) public DataSource cs1db () { return DruidDataSourceBuilder.create().build(); } @Bean(name = "cs2db") @ConfigurationProperties(prefix = "spring.datasource.druid.cs2-db" ) public DataSource cs2db () { return DruidDataSourceBuilder.create().build(); } /** * 動態(tài)數(shù)據(jù)源配置 * @return */ @Bean @Primary public DataSource multipleDataSource (@Qualifier("cs1db") DataSource cs1db, @Qualifier("cs2db") DataSource cs2db){ DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map< Object, Object > targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.cs1db.getValue(), cs1db ); targetDataSources.put(DBTypeEnum.cs2db.getValue(), cs2db); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(cs1db);//指定默認 return dynamicDataSource; } @Bean("sqlSessionFactory") public SqlSessionFactory sqlSessionFactory() throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(multipleDataSource(cs1db(),cs2db())); sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*/*Mapper.xml")); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setCacheEnabled(false); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setPlugins(new Interceptor[]{ mybatisPlusInterceptor() }); return sqlSessionFactory.getObject(); } }
通過覆蓋默認的DataSource進行加載動態(tài)數(shù)據(jù)源的配置信息,通過**multipleDataSource()方法設(shè)置數(shù)據(jù)源,最后通過sqlSessionFactory()**設(shè)置數(shù)據(jù)源的最終加載
8、編寫配置文件
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource cs1-db: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/short-chain?useUnicode=true&characterEncoding=UTF-8 username: root password: initialSize: 10 minIdle: 20 maxActive: 100 maxWait: 60000 cs2-db: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/short-chain?useUnicode=true&characterEncoding=UTF-8 username: root password: initialSize: 10 minIdle: 20 maxActive: 100 maxWait: 60000 maxActive: 100 maxWait: 60000
到此這篇關(guān)于Springboot+aop實現(xiàn)配置多數(shù)據(jù)源的示例代碼的文章就介紹到這了,更多相關(guān)Springboot aop多數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章

Java實現(xiàn)線程按序交替執(zhí)行的方法詳解