ディレクトリ
図1に示すように、ソースコード解析
多くの特定のアプリケーションシナリオでは、我々は、動的なデータソースを使用する必要がある場合。例えば、別個の読み出しと書き込み、および他のマルチテナントシナリオ。春ブーツ+ MyBatisの+ MySQLのに基づいて、このチュートリアルの場合は、達成するために。スプリングAbstractRoutingDataSource内蔵抽象クラスは、異なるキーに従ってデータソースマップ、戻り異なる複数のデータソースとして構成することができます。アプリケーションが最初のキーを設定することができるようにAbstractRoutingDataSourceもDataSourceインタフェースは、データベースにアクセスするために、実際のAbstractRoutingDataSourceから対応するデータソースは、指定されたデータベースへのアクセスを得ることができますので。次に、我々は、プロジェクト内の複数のデータソースを切り替えるソースによって解析しました:
抽象クラスAbstractRoutingDataSourceクラスのメンバーは次のよう。
private Map<Object, Object> targetDataSources;
private Object defaultTargetDataSource;
private Map<Object, DataSource> resolvedDataSources;
private DataSource resolvedDefaultDataSource;
- targetDataSourcesは、キーマッピングやデータベース接続が含まれています
- defaultTargetDataSourceは、デフォルトの接続を識別する
- resolvedDataSourcesこのデータ構造は、マッピング関係から構築され、データベース・ストレージ構造は、データソースとtargetDataSourcesによって識別されます
getConnection()メソッドを呼び出すことにより、データベースへの接続接続を作成するには、このクラスdetermineTargetDataSource()メソッド。
public Connection getConnection() throws SQLException {
return this.determineTargetDataSource().getConnection();
}
public Connection getConnection(String username, String password) throws SQLException {
return this.determineTargetDataSource().getConnection(username, password);
}
我々は、データ・ソースに接続された容器を春に決定determineTargetDataSource()メソッドを、見えます。、determineCurrentLookupKey()メソッドは抽象メソッドどちらが、我々は、このメソッドをオーバーライドするAbstractRoutingDataSource抽象クラスを継承する必要があります。このメソッドは、キーを返し、lookupKeyに割り当てられています。値は、データ・ソース・スイッチング機能を実現するようにキーは、キーresolvedDataSources DataSourceプロパティに対応する値によって取得することができることにより、のBEANNAME豆における鍵です。
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = this.determineCurrentLookupKey();
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
} else {
return dataSource;
}
}
私たちは、AbstractRoutingDataSource InitializingBeanインタフェースと実装afterPropertiesSetメソッドを実装し、このクラスを、見続けています。Beanが特定のBeanに対して行うことができる場合afterPropertiesSet初期化メソッドが実行されます。データソースBeanが動的に生成されるため、すべての必要がtargetDataSourcesに追加し、その後、豆の更新を通知するスプリングと、のための()afterPropertiesSetを呼び出します。したがって、このresolvedDataSourcesのtargetDataSourcesプロパティにキー情報格納属性における方法であって、その結果、後続のコールです。
public void afterPropertiesSet() {
if (this.targetDataSources == null) {
throw new IllegalArgumentException("Property 'targetDataSources' is required");
} else {
this.resolvedDataSources = new HashMap(this.targetDataSources.size());
this.targetDataSources.forEach((key, value) -> {
Object lookupKey = this.resolveSpecifiedLookupKey(key);
DataSource dataSource = this.resolveSpecifiedDataSource(value);
this.resolvedDataSources.put(lookupKey, dataSource);
});
if (this.defaultTargetDataSource != null) {
this.resolvedDefaultDataSource = this.resolveSpecifiedDataSource(this.defaultTargetDataSource);
}
}
}
2、プロジェクトが実施され
原理を分析した後、我々は、動的データ・ソースの切り替えは非常に簡単であると認識、最初の設定ファイルを変更する、2つのデータソースを追加することは、明らかである実際の状況に応じて構成され、マスタとスレーブは、単一のデータソースとフィールドの残りの部分と一致し、カスタマイズすることができます。
spring:
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123465
jdbc-url: jdbc:mysql://localhost:3306/theme_weixin?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123465
jdbc-url: jdbc:mysql://192.168.101.18:3306/theme_weixin?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
起動クラスを追加DataSourceAutoConfiguration.class = {}除外デフォルトの自動コンフィギュレーションデータソース無効、。デフォルトのデータ・ソースの構成が自動的にspring.datasourceを読み込む。あなたは、カスタマイズのために無効にしたいので、*属性は、データ・ソースを作成します。
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class WeixinApplication {
public static void main(String[] args) {
SpringApplication.run(WeixinApplication.class, args);
}
}
データ・ソース・タイプの設定、データ注入ソース構成属性、作成マスタ、スレーブデータソースを作成します
@Configuration
public class DataSourceConfig {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.master")
@RefreshScope
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave")
@RefreshScope
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
}
継承MybatisAutoConfigurationは、データ・ソースは、SqlSessionFactory複数の中に注入されます
@Configuration
public class MyBatisConfig extends MybatisAutoConfiguration {
@Resource(name = "masterDataSource")
private DataSource masterDataSource;
@Resource(name = "slaveDataSource")
private DataSource slaveDataSource;
public MyBatisConfig(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
super(properties, interceptorsProvider, typeHandlersProvider, languageDriversProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider);
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
// 重载父类 sqlSessionFactory init
return super.sqlSessionFactory(roundRobinDataSourceProxy());
}
private AbstractRoutingDataSource roundRobinDataSourceProxy() {
DynamicDataSource proxy = new DynamicDataSource();
Map<Object, Object> targetDataResources = new HashMap<>(2);
targetDataResources.put(DbContextHolder.DbType.MASTER,masterDataSource);
targetDataResources.put(DbContextHolder.DbType.SLAVE,slaveDataSource);
proxy.setDefaultTargetDataSource(masterDataSource);
proxy.setTargetDataSources(targetDataResources);
proxy.afterPropertiesSet();
return proxy;
}
}
DynamicDataSourceクラス、継承AbstractRoutingDataSource、書き換え方法のdetermineCurrentLookupKeyを作成します
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType().key();
}
}
DbContextHolderは次のとおりです。
public class DbContextHolder {
public enum DbType{
Master("masterDataSource"),
Slave("slaveDataSource");
String beanName;
DbType(String beanName) {
this.beanName = beanName;
}
public String key() {
return this.beanName;
}
}
private static final ThreadLocal<DbType> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDbType(DbType dbType){
if(dbType==null){
throw new NullPointerException();
}else{
CONTEXT_HOLDER.set(dbType);
}
}
public static DbType getDbType(){
return CONTEXT_HOLDER.get()==null? DbType.MASTER:CONTEXT_HOLDER.get();
}
public static void clearDbType() {
CONTEXT_HOLDER.remove();
}
}
データソースは、メソッド呼び出しに切り替える、または注釈モード切替をベースとすることができます。例示的なデータソース切替。
public String test() {
DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);
TabCategory tabCategory=new TabCategory();
tabCategory.setCategoryId(10L);
tabCategoryMapper.insertSelective(tabCategory);
DbContextHolder.clearDbType();
return "success";
}