SpringBoot(十一)-- 动态数据源

 2023-09-05 阅读 349 评论 0

摘要:SpringBoot中使用动态数据源可以实现分布式中的分库技术,比如查询用户 就在用户库中查询,查询订单 就在订单库中查询。 一、配置文件application.properties # 默认数据源 spring.datasource.url=jdbc:mysql://localhost:3306/consult spring.datasource.us

SpringBoot中使用动态数据源可以实现分布式中的分库技术,比如查询用户 就在用户库中查询,查询订单 就在订单库中查询。

一、配置文件application.properties

# 默认数据源
spring.datasource.url=jdbc:mysql://localhost:3306/consult
spring.datasource.username=myConsult
spring.datasource.password=123456
spring.datasource.driver-class-name=org.gjt.mm.mysql.Driver
# 更多数据源
custom.datasource.names=ds1,ds2 
custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1
custom.datasource.ds1.username=root 
custom.datasource.ds1.password=123456 
custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test2
custom.datasource.ds2.username=root 
custom.datasource.ds2.password=123456

二、pox.xml

 <!-- aspectj --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjtools</artifactId></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency>

三、使用aop自定义注解,实现动态切换数据源

1.动态数据源注册器

  1 package com.xsjt.dynamicDataSource;  
  2 import java.util.HashMap;
  3 import java.util.Map;
  4 import javax.sql.DataSource;
  5 import org.slf4j.Logger;
  6 import org.slf4j.LoggerFactory;
  7 import org.springframework.beans.MutablePropertyValues;
  8 import org.springframework.beans.PropertyValues;
  9 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 10 import org.springframework.beans.factory.support.GenericBeanDefinition;
 11 import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
 12 import org.springframework.boot.bind.RelaxedDataBinder;
 13 import org.springframework.boot.bind.RelaxedPropertyResolver;
 14 import org.springframework.context.EnvironmentAware;
 15 import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
 16 import org.springframework.core.convert.ConversionService;
 17 import org.springframework.core.convert.support.DefaultConversionService;
 18 import org.springframework.core.env.Environment;
 19 import org.springframework.core.type.AnnotationMetadata;
 20 /**  
 21  * ClassName:DynamicDataSourceRegister 
 22  * Date:     2017年11月13日 下午7:40:42
 23  * @author   Joe  
 24  * @version    
 25  * @since    JDK 1.8
 26  */
 27 public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
 28     private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
 29     
 30     private ConversionService conversionService = new DefaultConversionService();
 31     private PropertyValues dataSourcePropertyValues;
 32     
 33     // 如配置文件中未指定数据源类型,使用该默认值
 34     private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
 35     
 36 //     private static final Object DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
 37     
 38     // 数据源
 39     private DataSource defaultDataSource;
 40     
 41     private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();
 42     
 43     public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
 44         Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
 45         // 将主数据源添加到更多数据源中
 46         targetDataSources.put("dataSource", defaultDataSource);
 47         DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
 48         // 添加更多数据源
 49         targetDataSources.putAll(customDataSources);
 50         for (String key : customDataSources.keySet()) {
 51             DynamicDataSourceContextHolder.dataSourceIds.add(key);
 52         }
 53         
 54         // 创建DynamicDataSource
 55         GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
 56         beanDefinition.setBeanClass(DynamicDataSource.class);
 57         beanDefinition.setSynthetic(true);
 58         MutablePropertyValues mpv = beanDefinition.getPropertyValues();
 59         mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
 60         mpv.addPropertyValue("targetDataSources", targetDataSources);
 61         registry.registerBeanDefinition("dataSource", beanDefinition); // 注册到Spring容器中
 62         
 63         logger.info("Dynamic DataSource Registry");
 64     }
 65     
 66     /**
 67      * 创建DataSource
 68      * @param type
 69      * @param driverClassName
 70      * @param url
 71      * @param username
 72      * @param password
 73      * @return
 74      */
 75     @SuppressWarnings("unchecked")
 76     public DataSource buildDataSource(Map<String, Object> dsMap) {
 77         try {
 78             Object type = dsMap.get("type");
 79             if (type == null)
 80                 type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
 81                 
 82             Class<? extends DataSource> dataSourceType;
 83             dataSourceType = (Class<? extends DataSource>)Class.forName((String)type);
 84             
 85             String driverClassName = dsMap.get("driver-class-name").toString();
 86             String url = dsMap.get("url").toString();
 87             String username = dsMap.get("username").toString();
 88             String password = dsMap.get("password").toString();
 89             
 90             DataSourceBuilder factory = DataSourceBuilder.create()
 91                     .driverClassName(driverClassName)
 92                     .url(url)
 93                     .username(username)
 94                     .password(password)
 95                     .type(dataSourceType);
 96             return factory.build();
 97         }
 98         catch (ClassNotFoundException e) {
 99             e.printStackTrace();
100         }
101         return null;
102     }
103     
104     /**
105      * 加载多数据源配置
106      */
107     public void setEnvironment(Environment env) {
108         initDefaultDataSource(env);
109         initCustomDataSources(env);
110     }
111     
112     /**
113      * 初始化主数据源
114      */
115     private void initDefaultDataSource(Environment env) {
116         // 读取主数据源
117         RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
118         Map<String, Object> dsMap = new HashMap<String, Object>();
119         dsMap.put("type", propertyResolver.getProperty("type"));
120         dsMap.put("driver-class-name",propertyResolver.getProperty("driver-class-name"));
121         dsMap.put("url", propertyResolver.getProperty("url"));
122         dsMap.put("username", propertyResolver.getProperty("username"));
123         dsMap.put("password", propertyResolver.getProperty("password"));
124         defaultDataSource = buildDataSource(dsMap); 
125         dataBinder(defaultDataSource, env);
126     }
127     
128     /**
129      * 为DataSource绑定更多数据
130      * @param dataSource
131      * @param env
132      */
133     private void dataBinder(DataSource dataSource, Environment env) {
134         RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
135         //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
136         dataBinder.setConversionService(conversionService);
137         dataBinder.setIgnoreNestedProperties(false);//false
138         dataBinder.setIgnoreInvalidFields(false);//false
139         dataBinder.setIgnoreUnknownFields(true);//true
140         if (dataSourcePropertyValues == null) {
141             Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
142             Map<String, Object> values = new HashMap<String, Object>(rpr);
143             // 排除已经设置的属性
144             values.remove("type");
145             values.remove("driver-class-name");
146             values.remove("url");
147             values.remove("username");
148             values.remove("password");
149             dataSourcePropertyValues = new MutablePropertyValues(values);
150         }
151         dataBinder.bind(dataSourcePropertyValues);
152     }
153     
154     /**
155      * 初始化更多数据源
156      *
157      */
158     private void initCustomDataSources(Environment env) {
159         // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
160         RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver( env, "custom.datasource.");
161         String dsPrefixs = propertyResolver.getProperty("names");
162         for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
163             Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
164             DataSource ds = buildDataSource(dsMap);
165             customDataSources.put(dsPrefix, ds);
166             dataBinder(ds, env);
167         }
168     }
169 }
View Code

2.动态数据源适配器

package com.xsjt.dynamicDataSource;  
import java.util.ArrayList;
import java.util.List;
/**  * ClassName:DynamicDataSourceContextHolder * Date:     2017年11月13日 下午7:41:49* @author   Joe  * @version    * @since    JDK 1.8*/
public class DynamicDataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();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();}/*** 判断指定DataSrouce当前是否存在** @param dataSourceId* @return*/public static boolean containsDataSource(String dataSourceId){return dataSourceIds.contains(dataSourceId);}
}
View Code

3.自定义注解

/**  * ClassName:TargetDataSource * Date:     2017年11月13日 下午7:42:15* @author   Joe  * @version    * @since    JDK 1.8*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {String name();
}
View Code

4.动态数据源切面

package com.xsjt.dynamicDataSource;  
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**  * ClassName:DynamicDataSourceAspect * Date:     2017年11月13日 下午7:44:09* @author   Joe  * @version    * @since    JDK 1.8*/
@Aspect
//保证该AOP在@Transactional之前执行
@Order(-1)
@Component
public class DynamicDataSourceAspect {private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);/** * @Description 在方法执行之前执行  @annotation(ds) 会拦截有ds这个注解的方法即有 TargetDataSource这个注解的* @param @param point* @param @param ds* @param @throws Throwable 参数 * @return void 返回类型  * @throws */@Before("@annotation(ds)")public void changeDataSource(JoinPoint point, TargetDataSource ds)throws Throwable {String dsId = ds.name();if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());}else {logger.debug("Use DataSource : {} > {}", ds.name(),point.getSignature());DynamicDataSourceContextHolder.setDataSourceType(ds.name());}}/** * @Description 在方法执行之后执行  @annotation(ds) 会拦截有ds这个注解的方法即有 TargetDataSource这个注解的 * @param @param point* @param @param ds 参数 * @return void 返回类型  * @throws */@After("@annotation(ds)")public void restoreDataSource(JoinPoint point, TargetDataSource ds) {logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());DynamicDataSourceContextHolder.clearDataSourceType();}
}
View Code

5.继承Spring AbstractRoutingDataSource实现路由切换

package com.xsjt.dynamicDataSource;  
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**  * ClassName:DynamicDataSource * 继承Spring AbstractRoutingDataSource实现路由切换* Date:     2017年11月13日 下午7:49:49* @author   Joe  * @version    * @since    JDK 1.8*/
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}
}
View Code

四、怎么使用自定义的注解动态的切换数据源

只需要在service实现类中 对需要切换数据源的方法上 加上 自定义的注解即可,如:@TargetDataSource(name = "ds2")

五、启动类上添加@Import注解

  //注册动态多数据源
  @Import({DynamicDataSourceRegister.class})

六.源码下载

  https://gitee.com/xbq168/spring-boot-learn

 

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/239.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息