环境:
jdk 1.8
maven 3.6.0
springboot 2.6.6
druid 1.2.8
1.实现思路
DataSource是和线程绑定的,动态数据源的配置主要是通过继承AbstractRoutingDataSource类实现的,实现在AbstractRoutingDataSource类中的 protected Object determineCurrentLookupKey()方法来获取数据源,所以我们需要先创建一个多线程线程数据隔离的类来存放DataSource,然后在determineCurrentLookupKey()方法中通过这个类获取当前线程的DataSource,在=AbstractRoutingDataSource类中,DataSource是通过Key-value的方式保存的,我们可以通过ThreadLocal来保存Key,从而实现数据源的动态切换。
2.配置信息
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.6
com.example
demo
0.0.1-SNAPSHOT
demo
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
com.baomidou
mybatis-plus-boot-starter
3.4.0
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-aop
com.alibaba
druid-spring-boot-starter
1.2.8
com.alibaba
fastjson
1.2.70
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
3.具体代码
public class DynamicDataSourceContextHolder {
private static ThreadLocal threadLocal = new ThreadLocal<>();
public static List dataSourceIds = new ArrayList<>();
public static void setDataSourceType(String dataSourceType){
System.out.println("DynamicDataSourceContextHolder.setDataSourceType");
threadLocal.set(dataSourceType);
System.out.println("数据源修改为"+dataSourceType);
}
public static String getDataSourceType(){
System.out.println("DynamicDataSourceContextHolder.getDataSourceType");
return threadLocal.get();
}
public static void clearDataSourceType() {
System.out.println("DynamicDataSourceContextHolder.clearDataSourceType");
threadLocal.remove();
}
public static boolean containsDataSource(String dataSourceId) {
System.out.println("DynamicDataSourceContextHolder.containsDataSource");
return dataSourceIds.contains(dataSourceId);
}
}
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
System.out.println("DynamicDataSource.determineCurrentLookupKey");
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
/**
* 动态数据源注册
* 启动动态数据源请在启动类中 添加 @Import(DynamicDataSourceRegister.class)
*/
@Component
public class DynamicDataSourceRegister implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
// 如配置文件中未指定数据源类型,使用该默认值
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
// 数据源
private DataSource defaultDataSource;
private Map customDataSources = new HashMap();
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
System.out.println("DynamicDataSourceRegister.postProcessBeanDefinitionRegistry");
Map
@Component
@Aspect
@Order(-1)
public class DynamicDataSourceAspect {
@Before("@annotation(ds)")
public void changeDataSource(JoinPoint point, TargetDataSource ds) {
System.out.println("DynamicDataSourceAspect.changeDataSource");
String dsId = ds.name();
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
System.out.println("数据源"+ds.name()+"不存在,使用默认数据源 > "+point.getSignature());
} else {
System.out.println("Use DataSource :"+ds.name()+">"+point.getSignature());
DynamicDataSourceContextHolder.setDataSourceType(ds.name());
}
}
@After("@annotation(ds)")
public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
System.out.println("Revert DataSource :"+ds.name()+">"+point.getSignature());
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String name();
}
4.调用
@Data
public class Users {
private Long id;
private Long supervisorId;
private Long paGoalprivateId;
private Long cBpartnerId;
private String processing;
private Long cBpartnerLocationId;
private Long cGreetingId;
private Integer lastcontact;
private Integer birthday;
private Long adOrgtrxId;
private String isldapauthorized;
private String passwordhash;
private Integer isenabled;
private Integer isemployee;
private Integer isadmin;
private Integer usertype;
private String truename;
private String name;
private String description;
private String password;
private String email;
private String emailuser;
private String emailuserpw;
private String title;
private String comments;
private String phone;
private String phone2;
private String fax;
private String lastresult;
private String emailverify;
private Long cDepartmentId;
private String language;
private String isOtp;
private Integer otpLength;
private Integer otpSeconds;
private String otpSecret;
private BigDecimal otpCounter;
private String isOtpOnly;
private Date otpCdate;
private String loginIpRule;
private String issms;
private String isOut;
private Long assigneeId;
private String issaler;
private Long cStoreId;
private BigDecimal discountlimit;
private String isopr;
private String saasvendor;
private String saasuser;
private Long hrEmployeeId;
private Long cCustomerId;
private Long cCustomerupId;
private Long areamngId;
private Integer sgrade;
private Long cPriceregionId;
private Long cSupplierId;
private String subsystems;
private Date passwordexpirationdate;
private String passwordreset;
private String isret;
private String webposPer;
private String issys;
private String isSysUser;
private String smslogin;
private Long cMbrId;
private String pwdrand;
private Date lastlogindate;
private String lastloginip;
private String loginip;
private Date logindate;
private Long failedloginattempts;
private Long cAreaId;
private String wechatAuth;
private String qqAuth;
private String sinaAuth;
private String headimg;
private String employeeno;
private Integer ismanager;
private Integer groupsgrade;
private Long cpCHrorgId;
private String ecode;
private String ename;
private Long cpCDistribId;
private Long cpCStoreId;
private Long cpCSupplierId;
private String userstype;
private String f4;
private String rf;
private String pb1;
private String pb2;
private String pb3;
private String pb4;
private String pb5;
private String pb6;
private String remark;
private String mobil;
private String openid;
private Long cpCEmpId;
private String sex;
private String modifierename;
private String ownerename;
private Integer isdev;
private String wechat;
private String adyu;
private Long cpCPhyWarehouseId;
private String mac;
}
@Mapper
public interface UsersMapper extends BaseMapper {
@Select("select email_title as processing from ip_b_email where id = #{id}")
String getUser(@Param("id")Long id);
}
@Service
public class UsersService {
@Autowired
private UsersMapper usersMapper;
@TargetDataSource(name = "rds")
public String getUserById(Long id){
Users users = usersMapper.selectById(id);
String name = "";
if (null != users) {
name = users.getName();
}
return name;
}
@TargetDataSource(name = "mysql")
public String getUser(Long id){
return usersMapper.getUser(id);
}
}
@RestController
public class UserController {
@Autowired
private UsersService usersService;
@RequestMapping("/c/getUserName/{id}")
public String getUsersById(@PathVariable Long id){
return usersService.getUserById(id);
}
@RequestMapping("/c/getUser/{id}")
public String getUsers(@PathVariable Long id){
return usersService.getUser(id);
}
}
5.存在问题
工程启动正常,数据源切换不了,还是使用的默认数据源
6.问题解决
在注册数据源的时候会与com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure的类冲突报
The bean 'dataSource',could not be registered.A bean with that name has already been defined
要覆盖掉
beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
解决方案
在application配置文件里加一行:
spring.main.allow-bean-definition-overriding: true
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)