数据源是个象抽的接口，可以各种不同方式的玩转，容易混乱（尤其是"多数据源"和"动态数据源"）。需要一些约定，把概念固定下来。

### 1、三个数据源概念约定

以数据源在“容器”里的记录为基准。设计这个概念约定。


| 概念 | 约定 | 
| -------- | -------- | 
| 数据源           | 在容器里，会产生一个数据源的 Bean 记录。可以通过容器获取。     | 
| 多个数据源<br/>（或多数据源）     | 在容器里，会产生多个数据源的 Bean 记录。可以通过容器获取。     | 
| 子数据源        | 在一个数据源内部，还有二级数据源（也叫子数据源）。子数据源，不会在容器里有 Bean 记录。     | 


### 2、其它数据源概念



#### 分片数据源（内部有子数据源，基于分片规则切换）：

* 是指一个数据源内有多个子数据源，根据规则确定相关数据源。一般用于分库分表或读写分离场景等。
* 比如：ShardingDataSource（基于 Apache ShardingSphere 适配的数据源）

#### 动态数据源（内部有子数据源，基于线程状态切换）：

* 是指一个数据源内有多个子数据源，且可以动态切换内部的子数据源。用时，需要不断手动指定。
* 比如：DynamicDataSource，参考插件 [solon-data-dynamicds](/article/352) 
* 一般通过：`@DynamicDs("db_user_1")`、`@DynamicDs("db_user_2")` 切换动态数据源内部的子数据源
* <mark>一个应用里，只能有一个同类型的动态数据源！（否则，线程状态切换就错乱了）</mark>


### 3、“多数据源” 和 “动态数据源” 的区别

从技术上看 “分片数据源”、“动态数据源” 似乎也算 “多数据源”，内部都有多个子数据源。但，我们这里的 “多数据源” 以容器里的记录为准。

* 多数据源（或多个数据源）

是指在“容器”里有多个数据源的记录


* 动态数据源（相当于一个路由器数据源，或代理）

动态数据源，在“容器”里只会有一个数据源的记录。但内部会有多个子数据源。一般，可以通过线程状态进行切换。

### 4、数据源的获取方式

获取方式：


| 方式                        | 示例代码    | 
| ---------------- | -------- | 
| 注入方式                   | `@Inject("db_order")` 或者 `@Ds("db_order")`     | 
| 同步获取方式             | `Solon.context().getBean("db_order")`     | 
| 异步获取方式             | `Solon.context().getBeanAsync("db_order", ds->{ ... })`     | 
| 工具获取方式             | `DsUtils.observeDs(Solon.context(), "db_order", dsWrap->{ ... })` |

获取子数据源的方式（以 DynamicDataSource 为例）：

```java
//选获取数据源
DynamicDataSource dds = Solon.context().getBean("db_user");

//再获取数据源内的子数据源
DataSource tmp = dds.getTargetDataSource("db_user_w");
```

获取默认数据源的方式：


| 方式                        | 示例代码（按类型获取）    | 
| ---------------- | -------- | 
| 注入方式                   | `@Inject` 或者 `@Ds`     | 
| 同步获取方式             | `Solon.context().getBean(DataSource.class)`     | 
| 异步获取方式             | `Solon.context().getBeanAsync(DataSource.class, ds->{ ... })`     | 
| 工具获取方式             | `DsUtils.observeDs(Solon.context(), "", dsWrap->{ ... })` |



### 5、获取一批数据源


```java
//1.注入方式
@Inject 
Map<String, DataSource> dsMap;

@Inject
List<DataSource> dsList;

//2.订阅获取方式（可以源源不断，实时获取新建构的数据源）
Solon.context().subWrapsOfType(DataSource.class, dsWrap->{
    //dsWrap.name();   //数据源名字
    //dsWrap.typed();  //是否声明以类型注册的（相当于默认）
    //dsWrap.raw();    //原始 DataSource 实例
});

//3.同步获取方式（要注意时机点）
Map<String, DataSource> dsMap = Solon.context().getBeansMapOfType(DataSource.class);

List<DataSource> dsList = Solon.context().getBeansOfType(DataSource.class);
```

应用开发时，一般不会直接使用数据源对象，而是使用 ORM 的特定对象及增强注解。





### 6、插件适配指南（如何获取数据源）

工具 `DsUtils.observeDs`：

```java
/**
* @param dsName 数据源名（为空时，表过默认数据源）
*/
DsUtils.observeDs(appContext, dsName, (dsWrap) -> {
      
});
```

应用示例：(新增一个注解注入处理)

```java
public class DbBeanInjectorImpl implements BeanInjector<Db> {
    @Override
    public void doInject(VarHolder vh, Db anno) {
        //要求必须注入
        vh.required(true); 
        
        //根据注解获取数据源
        DsUtils.observeDs(vh.context(), anno.value(), (dsWrap) -> {
            inject0(vh, dsWrap);
        });
    }

    private void inject0(VarHolder vh, BeanWrap dsBw) {
        //...
    }
}
```

应用示例：(在 Ds 注解处理上，添加处理；可以共享 Ds 注解)

```java
DsInjector.getDefault().addHandler((vh, dsWrap) -> {
    //...
});
```

