dynamic-datasource-spring-boot-starter源码分析-动态加载数据库
地址:https://blog.csdn.net/chinawangfei/article/details/122830408
pom.xml
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.1</version> </dependency>
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
DynamicDataSourceAutoConfiguration.java
@Bean public DynamicDataSourceProvider ymlDynamicDataSourceProvider() { return new YmlDynamicDataSourceProvider(properties.getDatasource()); } @Bean @ConditionalOnMissingBean public DataSource dataSource() { DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(); dataSource.setPrimary(properties.getPrimary()); dataSource.setStrict(properties.getStrict()); dataSource.setStrategy(properties.getStrategy()); dataSource.setP6spy(properties.getP6spy()); dataSource.setSeata(properties.getSeata()); return dataSource; }
// 使用DS 动态获取
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX + ".aop", name = "enabled", havingValue = "true", matchIfMissing = true)
public Advisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
DynamicDatasourceAopProperties aopProperties = properties.getAop();
DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor(aopProperties.getAllowedPublicOnly(), dsProcessor);
DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor, DS.class);
advisor.setOrder(aopProperties.getOrder());
return advisor;
}
DynamicRoutingDataSource.java
@Override public void afterPropertiesSet() throws Exception { // 检查开启了配置但没有相关依赖 checkEnv(); // 添加并分组数据源 Map<String, DataSource> dataSources = new HashMap<>(16); for (DynamicDataSourceProvider provider : providers) { dataSources.putAll(provider.loadDataSources()); } for (Map.Entry<String, DataSource> dsItem : dataSources.entrySet()) { addDataSource(dsItem.getKey(), dsItem.getValue()); } // 检测默认数据源是否设置 if (groupDataSources.containsKey(primary)) { log.info("dynamic-datasource initial loaded [{}] datasource,primary group datasource named [{}]", dataSources.size(), primary); } else if (dataSourceMap.containsKey(primary)) { log.info("dynamic-datasource initial loaded [{}] datasource,primary datasource named [{}]", dataSources.size(), primary); } else { log.warn("dynamic-datasource initial loaded [{}] datasource,Please add your primary datasource or check your configuration", dataSources.size()); } }
DynamicDataSourceLoading.java 是自定义的数据源
/** * 子数据源加载*/ @Configuration public class DynamicDataSourceLoading { @Autowired SourceProperties sourceProperties; @Bean public DynamicDataSourceProvider jdbcDynamicDataSourceProvider() { return new AbstractJdbcDataSourceProvider(sourceProperties.getDriverClassName(), sourceProperties.getUrl(), sourceProperties.getUsername(), sourceProperties.getPassword()) { @Override protected Map<String, DataSourceProperty> executeStmt(Statement statement) throws SQLException { ResultSet rs = statement.executeQuery("select s.slave, s.username, s.password, s.url_prepend, s.url_append, s.driver_class_name from xy_tenant_source s where s.status = '0' and s.del_flag = '0'"); Map<String, DataSourceProperty> map = new HashMap<String, DataSourceProperty>(); while (rs.next()) { String name = rs.getString("slave"); String username = rs.getString("username"); String password = rs.getString("password"); String url = rs.getString("url_prepend").concat(StringUtils.trim(rs.getString("url_append"))); String driver = rs.getString("driver_class_name"); DataSourceProperty property = new DataSourceProperty(); property.setUsername(username); property.setPassword(password); property.setUrl(url); property.setDriverClassName(driver); map.put(name, property); } return map; } }; } }
DSUtils.java 自定义
public class DSUtils { protected static Logger logger = LoggerFactory.getLogger(DSUtils.class); /** * 添加一个数据源到数据源库中 * * @param source 数据源对象 */ public static void addDs(Source source) { try { DefaultDataSourceCreator dataSourceCreator = SpringUtils.getBean(DefaultDataSourceCreator.class); DataSourceProperty dataSourceProperty = new DataSourceProperty(); BeanUtils.copyProperties(source, dataSourceProperty); DataSource dataSource = SpringUtils.getBean(DataSource.class); DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; dataSource = dataSourceCreator.createDataSource(dataSourceProperty); ds.addDataSource(source.getSlave(), dataSource); } catch (Exception e) { e.printStackTrace(); throw new ServiceException("数据源添加失败"); } } /** * 从数据源库中删除一个数据源 * * @param slave 数据源编码 */ public static void delDs(String slave) { try { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) SpringUtils.getBean(DataSource.class); ds.removeDataSource(slave); } catch (Exception e) { e.printStackTrace(); throw new ServiceException("数据源删除失败"); } } /** * 获取当前数据源库中所有数据源 */ public static void getDs() { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) SpringUtils.getBean(DataSource.class); ds.getDataSources().keySet().forEach(System.out::println); } /** * 获取当前线程数据源名称 */ public static String getNowDsName() { return DynamicDataSourceContextHolder.peek(); }
MyDynamicDataSourceConfig.java 自定义
/** * 源访问策略注入*/ @Configuration @AutoConfigureBefore(DynamicDataSourceAutoConfiguration.class) public class MyDynamicDataSourceConfig { @Bean public DsProcessor dsProcessor() { DsHeaderProcessor headerProcessor = new DsHeaderProcessor(); DsSessionProcessor sessionProcessor = new DsSessionProcessor(); DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor(); DsIsolateExpressionProcessor isolateExpressionProcessor = new DsIsolateExpressionProcessor(); DsMainExpressionProcessor mainExpressionProcessor = new DsMainExpressionProcessor(); headerProcessor.setNextProcessor(sessionProcessor); sessionProcessor.setNextProcessor(spelExpressionProcessor); spelExpressionProcessor.setNextProcessor(isolateExpressionProcessor); isolateExpressionProcessor.setNextProcessor(mainExpressionProcessor); return headerProcessor; } /** * 解决warn- discard long time none received connection xxx * druid会优先使用pingMethod方法来检查空闲连接 * 通过设置druid.mysql.usePingMethod=false,让其使用validationQuery语句 */ @PostConstruct public void setProperty() { System.setProperty("druid.mysql.usePingMethod","false"); } }
@Component public class DsMainExpressionProcessor extends DsProcessor { @Override public boolean matches(String key) { return key.startsWith("#main"); } @Override public String doDetermineDatasource(MethodInvocation invocation, String key) { return TenantConstants.Source.MAIN.getCode(); } } @Component public class DsIsolateExpressionProcessor extends DsProcessor { @Override public boolean matches(String key) { return key.startsWith("#isolate"); } @Override public String doDetermineDatasource(MethodInvocation invocation, String key) { String sourceName = SecurityUtils.getSourceName(); return StrUtil.isNotEmpty(sourceName) ? sourceName : DSUtils.getNowDsName(); } }