Spring中的动态数据源解读

 更新时间:2023年06月27日 09:16:24   作者:daliucheng  
这篇文章主要介绍了关于Spring中的动态数据源解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

动态数据源的原理得先说清。

原理

平常在使用Mysql的时候是通过JDBC的,得给一个url,userName,和password,如下:

jdbc:mysql://localhost:3306/t_db1?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC

一个url对应一个Connection对象,需要在url中指定需要连接的库。

之后的Mysql的CRUD的操作都是通过Connection对象来做的。所以,动态,就是在这个时候操作的,在获取Connection的时候来通过指定的key来判断要用哪个数据源。这得有一个前提,得先建立Connection,之后在获取Connection的时候在判断,

1.通过什么判断呢?存放key和Connection的数据结构是什么?

只要指定一个回调方法,key随便,存放key和Connection的数据结构是Map。在获取Connection的时候,先确定Key,在获取Connection就好了。

再看Spring

Java连接Mysql指定了接口,需要实现DataSource接口,Spring中提供了抽象类(AbstractDataSource

解释说明

DriverManagerDataSource

这个没有什么可说,只是封装了一下连接mysql需要用的一些属性,比如userName,password,url,driver,就是就老一套的获取Connection封装了一下

AbstractRoutingDataSource

Spring提供的动态数据源的抽象类

首先,它里面有一个Map,Map的key是Object(这是自定义的),V是Object(虽然是Object,代码里面规定了,只能为String,和DataSource),其实它本质就应该是DataSource,这里是String的目的是为了可以通过DataSourceLookup来获取数据源。(比如,可以在配置数据源的时候,直接配置数据源的bean的名字,然后自己写一个DataSourceLookup的实现类,从BeanFactory中获取DataSource)。

还提供了一个determineCurrentLookupKey()方法来判断当前数据源的key,其实就是map中的key。在最终获取Connection的时候通过当前的key获取DataSource,在通过DataSource获取真正的Connection。

代码分析

1.属性

2.实现接口

实现了InitializingBean那它肯定在afterPropertiesSet会做操作。等会看看

3.重要方法

afterPropertiesSet

获取Connection之前确定Key

 `determineCurrentLookupKey`方法是留给子类拓展的。

determineCurrentLookupKey怎么来确定key呢?

它是一个无参的方法,一般来说,都是放在ThreadLocal中,在执行sql操作之前,在对应的ThreadLocal放这次需要的key,就可以在determineCurrentLookupKey中获取ThreadLocal中的值,确定key。

 <font color='red'>Spring提供了一个动态数据源的实现类,`IsolationLevelDataSourceRouter`,key是事务的隔离级别。意思就是可以根据不同的隔离级别来选择DataSource</font>

 还有,一般都是用切面来操作ThreadLocal的

例子

我这里为了简单,就不写切面了。直接代码cv。

1.配置类

package datasource.dynamic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class Config {
	@Bean
	public DriverManagerDataSource dataSource1(){
		String url = "jdbc:mysql://localhost:3306/t_db1?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
		String userName = "root";
		String password = "123456";
		return new DriverManagerDataSource(url,userName,password);
	}
	@Bean
	public DriverManagerDataSource dataSource2(){
		String url = "jdbc:mysql://localhost:3306/t_db2?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
		String userName = "root";
		String password = "123456";
		return new DriverManagerDataSource(url,userName,password);
	}
	@Bean
	public DriverManagerDataSource dataSource3(){
		String url = "jdbc:mysql://localhost:3306/t_db3?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
		String userName = "root";
		String password = "123456";
		return new DriverManagerDataSource(url,userName,password);
	}
	@Bean
	public MyDynamicDataSource dynamicDataSource(Map<String,DriverManagerDataSource> map){
		HashMap<Object, Object> original = new HashMap<>(map);
		MyDynamicDataSource myDynamicDataSource = new MyDynamicDataSource();
		myDynamicDataSource.setTargetDataSources(original);
		return myDynamicDataSource;
	}
	@Bean
	public JdbcTemplate jdbcTemplate(AbstractRoutingDataSource dataSource){
		return new JdbcTemplate(dataSource);
	}
}

配置了三个数据库,也就是三个数据源,利用JdbcTemplate来快捷的执行sql,JdbcTemplate需要指定用自己定义的动态数据源(MyDynamicDataSource)。

此外,之前说了,动态数据源里面有个map,它得需要数据源,此外还需要key,这里用bean的名字做为key,创建MyDynamicDataSource之后,设置TargetDataSources。当MyDynamicDataSource被Spring创建的时候,InitializingBean#afterPropertiesSet()会通过DataSourceLookup转换。

2.ThreadLocal

package datasource.dynamic;
import org.springframework.util.Assert;
public class DataSourceKeyHolder {
	private static final ThreadLocal<String> keyHolder = new ThreadLocal<>();
	public static void setKey(String key) {
		keyHolder.remove();
		keyHolder.set(key);
	}
	public static String getKey() {
		String s = keyHolder.get();
		Assert.notNull(s, "key 不能为空");
		return s;
	}
	public static void clear(){
		keyHolder.remove();
	}
}

3.动态数据源实现类

package datasource.dynamic;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class MyDynamicDataSource extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		return DataSourceKeyHolder.getKey();
	}
}

只是从ThreadLocal中获取当前的key就好了。

4.实体对象

public class TestBean {
	private Integer id;
	private String name;
	private Double age;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getAge() {
		return age;
	}
	public void setAge(Double age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "TestBean{" +
				"id=" + id +
				", name='" + name + '\'' +
				", age=" + age +
				'}';
	}
}

5.sql

-- auto-generated definition
create table t_test
(
    id   int auto_increment
        primary key,
    name text   null,
    age  double null
);

创建三个数据库,在三个数据库里面都创建这个表,填写点数据。

6.主要测试类

package datasource.dynamic;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class MainTest {
	public static void main(String[] args) {
		try {
			AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
			JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class); // 这里从头到尾用的都是一个JdbcTemplate
			dataSource1(jdbcTemplate); // 动态数据源1
			dataSource2(jdbcTemplate);// 动态数据源2
			dataSource3(jdbcTemplate);// 动态数据源3
		}catch (Exception e){
			e.printStackTrace();
		}
	}
	public static void dataSource1(	JdbcTemplate jdbcTemplate ){
		DataSourceKeyHolder.setKey("dataSource1");
		try {
			TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1);
			System.out.println(testBean);
		}finally {
			DataSourceKeyHolder.clear();
		}
	}
	public static void dataSource2(	JdbcTemplate jdbcTemplate ){
		DataSourceKeyHolder.setKey("dataSource2");
		try {
			TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1);
			System.out.println(testBean);
		}finally {
			DataSourceKeyHolder.clear();
		}
	}
	public static void dataSource3(	JdbcTemplate jdbcTemplate ){
		DataSourceKeyHolder.setKey("dataSource3");
		try {
			TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1);
			System.out.println(testBean);
		}finally {
			DataSourceKeyHolder.clear();
		}
	}
}

7.结果

总结

关于这篇文章,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Netty序列化深入理解与使用

    Netty序列化深入理解与使用

    序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象
    2022-08-08
  • Spring整合消息队列RabbitMQ流程

    Spring整合消息队列RabbitMQ流程

    Spring整合RabbitMQ很容易,但是整合的目的是为了使用,那要使用RabbitMQ就要对其有一定的了解,不然容易整成一团浆糊。因为说到底,Spring只是在封装RabbitMQ的API,让其更容易使用而已,废话不多说,让我们一起整它
    2023-03-03
  • spring源码阅读--aop实现原理讲解

    spring源码阅读--aop实现原理讲解

    这篇文章主要介绍了spring源码阅读--aop实现原理讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • JVM与操作系统之间的关系详解

    JVM与操作系统之间的关系详解

    JVM与操作系统之间是依赖与被依赖的关系,JVM依赖于操作系统提供的资源和服务,同时JVM也起到了抽象与隔离的作用,为Java程序提供了一个统一的、与平台无关的运行环境,提高了Java程序的安全性
    2025-03-03
  • Java单例模式实现静态内部类方法示例

    Java单例模式实现静态内部类方法示例

    这篇文章主要介绍了Java单例模式实现静态内部类方法示例,涉及构造函数私有化等相关内容,需要的朋友可以了解下。
    2017-09-09
  • 详解JNI到底是什么

    详解JNI到底是什么

    JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互
    2021-06-06
  • Java如何实现登录token令牌

    Java如何实现登录token令牌

    这篇文章主要介绍了Java如何实现登录token令牌,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • Java中ClassLoader类加载学习总结

    Java中ClassLoader类加载学习总结

    本篇文章主要给大家讲述了Java中ClassLoader类加载的原理以及用法总结,一起学习下。
    2017-12-12
  • IDEA 当前在线人数和历史访问量的示例代码

    IDEA 当前在线人数和历史访问量的示例代码

    这篇文章主要介绍了IDEA 当前在线人数和历史访问量的实例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • 关于SpringBoot改动后0.03秒启动的问题

    关于SpringBoot改动后0.03秒启动的问题

    这篇文章主要介绍了SpringBoot改动后0.03秒启动,本文结合示例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12

最新评论