001/* 002 * Copyright (c) 2022-2024, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.spring.boot; 017 018import com.mybatisflex.core.datasource.DataSourceBuilder; 019import com.mybatisflex.core.datasource.DataSourceDecipher; 020import com.mybatisflex.core.datasource.DataSourceManager; 021import com.mybatisflex.core.datasource.FlexDataSource; 022import com.mybatisflex.core.exception.FlexExceptions; 023import com.mybatisflex.core.util.MapUtil; 024import com.mybatisflex.spring.boot.MybatisFlexProperties.SeataConfig; 025import com.mybatisflex.spring.datasource.DataSourceAdvice; 026import io.seata.rm.datasource.DataSourceProxy; 027import io.seata.rm.datasource.xa.DataSourceProxyXA; 028import org.apache.ibatis.session.SqlSessionFactory; 029import org.mybatis.spring.SqlSessionFactoryBean; 030import org.springframework.beans.factory.ObjectProvider; 031import org.springframework.beans.factory.config.BeanDefinition; 032import org.springframework.boot.autoconfigure.AutoConfigureBefore; 033import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 034import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 035import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 036import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; 037import org.springframework.boot.context.properties.EnableConfigurationProperties; 038import org.springframework.context.annotation.Bean; 039import org.springframework.context.annotation.Configuration; 040import org.springframework.context.annotation.Role; 041 042import javax.sql.DataSource; 043import java.util.Map; 044 045/** 046 * MyBatis-Flex 多数据源的配置支持。 047 * 048 * @author michael 049 * @author 王帅 050 */ 051@ConditionalOnMybatisFlexDatasource() 052@Configuration(proxyBeanMethods = false) 053@EnableConfigurationProperties(MybatisFlexProperties.class) 054@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) 055@AutoConfigureBefore(value = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class} 056 , name = {"com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure", 057 "com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure"}) 058public class MultiDataSourceAutoConfiguration { 059 060 private final String master; 061 062 private final Map<String, Map<String, String>> dataSourceProperties; 063 064 private final SeataConfig seataConfig; 065 066 // 数据源解密器 067 protected final DataSourceDecipher dataSourceDecipher; 068 069 070 public MultiDataSourceAutoConfiguration(MybatisFlexProperties properties 071 , ObjectProvider<DataSourceDecipher> dataSourceDecipherProvider 072 ) { 073 dataSourceProperties = properties.getDatasource(); 074 dataSourceDecipher = dataSourceDecipherProvider.getIfAvailable(); 075 seataConfig = properties.getSeataConfig(); 076 master = properties.getDefaultDatasourceKey(); 077 } 078 079 @Bean 080 @ConditionalOnMissingBean 081 public DataSource dataSource() { 082 083 FlexDataSource flexDataSource = null; 084 085 if (dataSourceProperties != null && !dataSourceProperties.isEmpty()) { 086 087 if (dataSourceDecipher != null) { 088 DataSourceManager.setDecipher(dataSourceDecipher); 089 } 090 091 if (master != null) { 092 Map<String, String> map = dataSourceProperties.remove(master); 093 if (map != null) { 094 flexDataSource = addDataSource(MapUtil.entry(master, map), flexDataSource); 095 } else { 096 throw FlexExceptions.wrap("没有找到默认数据源 \"%s\" 对应的配置,请检查您的多数据源配置。", master); 097 } 098 } 099 100 for (Map.Entry<String, Map<String, String>> entry : dataSourceProperties.entrySet()) { 101 flexDataSource = addDataSource(entry, flexDataSource); 102 } 103 } 104 105 return flexDataSource; 106 } 107 108 private FlexDataSource addDataSource(Map.Entry<String, Map<String, String>> entry, FlexDataSource flexDataSource) { 109 DataSource dataSource = new DataSourceBuilder(entry.getValue()).build(); 110 DataSourceManager.decryptDataSource(dataSource); 111 112 if (seataConfig != null && seataConfig.isEnable()) { 113 if (seataConfig.getSeataMode() == MybatisFlexProperties.SeataMode.XA) { 114 dataSource = new DataSourceProxyXA(dataSource); 115 } else { 116 dataSource = new DataSourceProxy(dataSource); 117 } 118 } 119 120 if (flexDataSource == null) { 121 flexDataSource = new FlexDataSource(entry.getKey(), dataSource, false); 122 } else { 123 flexDataSource.addDataSource(entry.getKey(), dataSource, false); 124 } 125 return flexDataSource; 126 } 127 128 129 /** 130 * {@link com.mybatisflex.annotation.UseDataSource} 注解切换数据源切面。 131 */ 132 @Bean 133 @ConditionalOnMissingBean 134 @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 135 public DataSourceAdvice dataSourceAdvice() { 136 return new DataSourceAdvice(); 137 } 138 139}