fix bugs
This commit is contained in:
48
blade-starter-tenant-dynamic/pom.xml
Normal file
48
blade-starter-tenant-dynamic/pom.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>BladeX-Tool</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>blade-starter-tenant-dynamic</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${project.parent.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!--Blade-->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-starter-tenant</artifactId>
|
||||
</dependency>
|
||||
<!-- Druid -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!--Dynamic-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- sharding-jdbc -->
|
||||
<dependency>
|
||||
<groupId>org.apache.shardingsphere</groupId>
|
||||
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Auto -->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-auto</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.config;
|
||||
|
||||
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationAdvisor;
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
|
||||
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.creator.druid.DruidDataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.event.DataSourceInitEvent;
|
||||
import com.baomidou.dynamic.datasource.event.EncDataSourceInitEvent;
|
||||
import com.baomidou.dynamic.datasource.processor.DsJakartaHeaderProcessor;
|
||||
import com.baomidou.dynamic.datasource.processor.DsJakartaSessionProcessor;
|
||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||
import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor;
|
||||
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceCreatorAutoConfiguration;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.tenant.dynamic.*;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springblade.core.tenant.constant.TenantBaseConstant.TENANT_DYNAMIC_DATASOURCE_PROP;
|
||||
import static org.springblade.core.tenant.constant.TenantBaseConstant.TENANT_DYNAMIC_GLOBAL_PROP;
|
||||
|
||||
/**
|
||||
* 多租户数据源配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@EnableConfigurationProperties({DataSourceProperties.class, DynamicDataSourceProperties.class})
|
||||
@AutoConfiguration(before = {DruidDataSourceAutoConfigure.class, DynamicDataSourceAutoConfiguration.class})
|
||||
@Import(value = {DynamicDataSourceCreatorAutoConfiguration.class})
|
||||
@ConditionalOnProperty(value = TENANT_DYNAMIC_DATASOURCE_PROP, havingValue = "true")
|
||||
public class TenantDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DataSourceInitEvent dataSourceInitEvent() {
|
||||
return new EncDataSourceInitEvent();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DefaultDataSourceCreator dataSourceCreator(List<DataSourceCreator> dataSourceCreators, DataSourceInitEvent dataSourceInitEvent, DynamicDataSourceProperties properties) {
|
||||
DefaultDataSourceCreator creator = new DefaultDataSourceCreator();
|
||||
creator.setCreators(dataSourceCreators);
|
||||
creator.setDataSourceInitEvent(dataSourceInitEvent);
|
||||
creator.setPublicKey(properties.getPublicKey());
|
||||
creator.setLazy(properties.getLazy());
|
||||
creator.setP6spy(properties.getP6spy());
|
||||
creator.setSeata(properties.getSeata());
|
||||
creator.setSeataMode(properties.getSeataMode());
|
||||
return creator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public DynamicDataSourceProvider dynamicDataSourceProvider(DefaultDataSourceCreator dataSourceCreator, DataSourceProperties dataSourceProperties, DynamicDataSourceProperties dynamicDataSourceProperties) {
|
||||
String driverClassName = dataSourceProperties.getDriverClassName();
|
||||
String url = dataSourceProperties.getUrl();
|
||||
String username = dataSourceProperties.getUsername();
|
||||
String password = dataSourceProperties.getPassword();
|
||||
DataSourceProperty master = dynamicDataSourceProperties.getDatasource().get(dynamicDataSourceProperties.getPrimary());
|
||||
if (master != null) {
|
||||
driverClassName = master.getDriverClassName();
|
||||
url = master.getUrl();
|
||||
username = master.getUsername();
|
||||
password = master.getPassword();
|
||||
}
|
||||
return new TenantDataSourceJdbcProvider(dataSourceCreator, dynamicDataSourceProperties, driverClassName, url, username, password);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public DataSource dataSource(List<DynamicDataSourceProvider> providers, DynamicDataSourceProperties dynamicDataSourceProperties) {
|
||||
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(providers);
|
||||
dataSource.setPrimary(dynamicDataSourceProperties.getPrimary());
|
||||
dataSource.setStrict(dynamicDataSourceProperties.getStrict());
|
||||
dataSource.setStrategy(dynamicDataSourceProperties.getStrategy());
|
||||
dataSource.setP6spy(dynamicDataSourceProperties.getP6spy());
|
||||
dataSource.setSeata(dynamicDataSourceProperties.getSeata());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TenantDataSourceAnnotationInterceptor tenantDataSourceAnnotationInterceptor(DsProcessor dsProcessor, DynamicDataSourceProperties dynamicDataSourceProperties) {
|
||||
return new TenantDataSourceAnnotationInterceptor(dynamicDataSourceProperties.getAop().getAllowedPublicOnly(), dsProcessor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Role(value = BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(TenantDataSourceAnnotationInterceptor tenantDataSourceAnnotationInterceptor, DynamicDataSourceProperties dynamicDataSourceProperties) {
|
||||
DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(tenantDataSourceAnnotationInterceptor, DS.class);
|
||||
advisor.setOrder(dynamicDataSourceProperties.getAop().getOrder());
|
||||
return advisor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(value = TENANT_DYNAMIC_GLOBAL_PROP, havingValue = "true")
|
||||
public TenantDataSourceGlobalInterceptor tenantDataSourceGlobalInterceptor() {
|
||||
return new TenantDataSourceGlobalInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Role(value = BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
@ConditionalOnProperty(value = TENANT_DYNAMIC_GLOBAL_PROP, havingValue = "true")
|
||||
public TenantDataSourceGlobalAdvisor tenantDataSourceGlobalAdvisor(TenantDataSourceGlobalInterceptor tenantDataSourceGlobalInterceptor, DynamicDataSourceProperties dynamicDataSourceProperties) {
|
||||
TenantDataSourceGlobalAdvisor advisor = new TenantDataSourceGlobalAdvisor(tenantDataSourceGlobalInterceptor);
|
||||
advisor.setOrder(dynamicDataSourceProperties.getAop().getOrder() + 1);
|
||||
return advisor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DsProcessor dsProcessor() {
|
||||
DsProcessor headerProcessor = new DsJakartaHeaderProcessor();
|
||||
DsProcessor sessionProcessor = new DsJakartaSessionProcessor();
|
||||
DsTenantIdProcessor tenantIdProcessor = new DsTenantIdProcessor();
|
||||
DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
|
||||
headerProcessor.setNextProcessor(sessionProcessor);
|
||||
sessionProcessor.setNextProcessor(tenantIdProcessor);
|
||||
tenantIdProcessor.setNextProcessor(spelExpressionProcessor);
|
||||
return headerProcessor;
|
||||
}
|
||||
|
||||
@Order
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnProperty(value = TENANT_DYNAMIC_DATASOURCE_PROP, havingValue = "true")
|
||||
public static class TenantDataSourceAnnotationConfiguration implements SmartInitializingSingleton {
|
||||
|
||||
private final TenantDataSourceAnnotationInterceptor tenantDataSourceAnnotationInterceptor;
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final DruidDataSourceCreator dataSourceCreator;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
TenantDataSourceHolder tenantDataSourceHolder = new TenantDataSourceHolder(dataSource, dataSourceCreator, jdbcTemplate);
|
||||
tenantDataSourceAnnotationInterceptor.setHolder(tenantDataSourceHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Order
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnProperty(value = TENANT_DYNAMIC_GLOBAL_PROP, havingValue = "true")
|
||||
public static class TenantDataSourceGlobalConfiguration implements SmartInitializingSingleton {
|
||||
|
||||
private final TenantDataSourceGlobalInterceptor tenantDataSourceGlobalInterceptor;
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final DruidDataSourceCreator dataSourceCreator;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
TenantDataSourceHolder tenantDataSourceHolder = new TenantDataSourceHolder(dataSource, dataSourceCreator, jdbcTemplate);
|
||||
tenantDataSourceGlobalInterceptor.setHolder(tenantDataSourceHolder);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
|
||||
/**
|
||||
* 租户动态数据源解析器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class DsTenantIdProcessor extends DsProcessor {
|
||||
|
||||
public static final String TENANT_ID_KEY = "#token.tenantId";
|
||||
|
||||
@Override
|
||||
public boolean matches(String key) {
|
||||
return key.equals(TENANT_ID_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String doDetermineDatasource(MethodInvocation invocation, String key) {
|
||||
return AuthUtil.getTenantId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 租户数据源
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
public class TenantDataSource {
|
||||
|
||||
/**
|
||||
* 数据源类型
|
||||
*/
|
||||
private int category;
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private String tenantId;
|
||||
/**
|
||||
* 数据源ID
|
||||
*/
|
||||
private String datasourceId;
|
||||
/**
|
||||
* 驱动类
|
||||
*/
|
||||
private String driverClass;
|
||||
/**
|
||||
* 数据库链接
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 数据库账号名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 数据库密码
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* 分库分表配置
|
||||
*/
|
||||
private String shardingConfig;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor;
|
||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||
import lombok.Setter;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tenant.exception.TenantDataSourceException;
|
||||
|
||||
/**
|
||||
* 租户数据源切换拦截器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class TenantDataSourceAnnotationInterceptor extends DynamicDataSourceAnnotationInterceptor {
|
||||
|
||||
@Setter
|
||||
private TenantDataSourceHolder holder;
|
||||
|
||||
public TenantDataSourceAnnotationInterceptor(Boolean allowedPublicOnly, DsProcessor dsProcessor) {
|
||||
super(allowedPublicOnly, dsProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
try {
|
||||
holder.handleDataSource(AuthUtil.getTenantId());
|
||||
return super.invoke(invocation);
|
||||
} catch (Exception exception) {
|
||||
throw new TenantDataSourceException(exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import static org.springblade.core.launch.constant.AppConstant.BASE_PACKAGES;
|
||||
|
||||
/**
|
||||
* 租户数据源全局处理器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class TenantDataSourceGlobalAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||
|
||||
private final Advice advice;
|
||||
|
||||
private final Pointcut pointcut;
|
||||
|
||||
public TenantDataSourceGlobalAdvisor(@NonNull TenantDataSourceGlobalInterceptor tenantDataSourceGlobalInterceptor) {
|
||||
this.advice = tenantDataSourceGlobalInterceptor;
|
||||
this.pointcut = buildPointcut();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Advice getAdvice() {
|
||||
return this.advice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
|
||||
if (this.advice instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
private Pointcut buildPointcut() {
|
||||
AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
|
||||
cut.setExpression(
|
||||
"(@within(org.springframework.stereotype.Controller) || @within(org.springframework.web.bind.annotation.RestController)) && " +
|
||||
"(!@annotation(" + BASE_PACKAGES + ".core.tenant.annotation.NonDS) && !@within(" + BASE_PACKAGES + ".core.tenant.annotation.NonDS))"
|
||||
);
|
||||
return new ComposablePointcut((Pointcut) cut);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||
import lombok.Setter;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tenant.exception.TenantDataSourceException;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
/**
|
||||
* 租户数据源全局拦截器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class TenantDataSourceGlobalInterceptor implements MethodInterceptor {
|
||||
|
||||
@Setter
|
||||
private TenantDataSourceHolder holder;
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
String tenantId = AuthUtil.getTenantId();
|
||||
try {
|
||||
if (StringUtil.isNotBlank(tenantId)) {
|
||||
holder.handleDataSource(tenantId);
|
||||
DynamicDataSourceContextHolder.push(tenantId);
|
||||
}
|
||||
return invocation.proceed();
|
||||
} catch (Exception exception) {
|
||||
throw new TenantDataSourceException(exception.getMessage());
|
||||
} finally {
|
||||
if (StringUtil.isNotBlank(tenantId)) {
|
||||
DynamicDataSourceContextHolder.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
|
||||
import com.baomidou.dynamic.datasource.creator.druid.DruidConfig;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.cache.utils.CacheUtil;
|
||||
import org.springblade.core.tenant.utils.ShardingUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springblade.core.tenant.constant.TenantBaseConstant.*;
|
||||
|
||||
/**
|
||||
* 租户数据源核心处理类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class TenantDataSourceHolder {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final DataSourceCreator dataSourceCreator;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 数据源缓存处理
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
public void handleDataSource(String tenantId) {
|
||||
// 获取储存的数据源集合
|
||||
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
|
||||
Set<String> keys = ds.getDataSources().keySet();
|
||||
// 配置不存在则动态添加数据源,以懒加载的模式解决分布式场景的配置同步
|
||||
// 为了保证数据完整性,配置后生成数据源缓存,后台便无法修改更换数据源,若一定要修改请迁移数据后重启服务或自行修改底层逻辑
|
||||
if (!keys.contains(tenantId)) {
|
||||
TenantDataSource tenantDataSource = getDataSource(tenantId);
|
||||
if (tenantDataSource != null) {
|
||||
int category = tenantDataSource.getCategory();
|
||||
if (category == JDBC_CATEGORY) {
|
||||
// 创建数据源配置
|
||||
DataSourceProperty dataSourceProperty = new DataSourceProperty();
|
||||
// 拷贝数据源配置
|
||||
BeanUtils.copyProperties(tenantDataSource, dataSourceProperty);
|
||||
// 设置驱动类
|
||||
dataSourceProperty.setDriverClassName(tenantDataSource.getDriverClass());
|
||||
// 关闭懒加载
|
||||
dataSourceProperty.setLazy(Boolean.FALSE);
|
||||
// 设置SQL校验
|
||||
DruidConfig druid = dataSourceProperty.getDruid();
|
||||
if (StringUtil.equals(dataSourceProperty.getDriverClassName(), ORACLE_DRIVER_CLASS)) {
|
||||
druid.setValidationQuery(ORACLE_VALIDATE_STATEMENT);
|
||||
} else {
|
||||
druid.setValidationQuery(COMMON_VALIDATE_STATEMENT);
|
||||
}
|
||||
// 创建Jdbc数据源
|
||||
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
|
||||
// 添加最新数据源
|
||||
ds.addDataSource(tenantId, dataSource);
|
||||
} else if (category == SHARDING_CATEGORY) {
|
||||
// 创建Sharding数据源
|
||||
DataSource dataSource = ShardingUtil.createDataSource(tenantDataSource.getShardingConfig());
|
||||
// 添加最新数据源
|
||||
ds.addDataSource(tenantId, dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断租户是否有数据源配置
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
private Boolean existDataSource(String tenantId) {
|
||||
// 将租户是否配置数据源进行缓存,若重新配置会将此缓存清空并在下次请求的时候懒加载
|
||||
// 若租户没有配置数据源则会自动使用master数据源,此举是为了避免在没有数据库的时候频繁查询导致缓存击穿
|
||||
Boolean exist = CacheUtil.get(TENANT_DATASOURCE_CACHE, TENANT_DATASOURCE_EXIST_KEY, tenantId, Boolean.class, Boolean.FALSE);
|
||||
if (exist == null) {
|
||||
TenantDataSource tenantDataSource = jdbcTemplate.queryForObject(TENANT_DATASOURCE_EXIST_STATEMENT, new String[]{tenantId}, new BeanPropertyRowMapper<>(TenantDataSource.class));
|
||||
if (tenantDataSource != null && StringUtil.isNotBlank(tenantDataSource.getDatasourceId())) {
|
||||
exist = Boolean.TRUE;
|
||||
} else {
|
||||
exist = Boolean.FALSE;
|
||||
}
|
||||
CacheUtil.put(TENANT_DATASOURCE_CACHE, TENANT_DATASOURCE_EXIST_KEY, tenantId, exist, Boolean.FALSE);
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应的数据源配置
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
private TenantDataSource getDataSource(String tenantId) {
|
||||
// 不存在租户数据源则返回空,防止缓存击穿
|
||||
if (!existDataSource(tenantId)) {
|
||||
return null;
|
||||
}
|
||||
// 获取租户数据源信息
|
||||
TenantDataSource tenantDataSource = CacheUtil.get(TENANT_DATASOURCE_CACHE, TENANT_DATASOURCE_KEY, tenantId, TenantDataSource.class, Boolean.FALSE);
|
||||
if (tenantDataSource == null) {
|
||||
tenantDataSource = jdbcTemplate.queryForObject(TENANT_DATASOURCE_SINGLE_STATEMENT, new String[]{tenantId}, new BeanPropertyRowMapper<>(TenantDataSource.class));
|
||||
if (tenantDataSource != null && StringUtil.isNoneBlank(tenantDataSource.getTenantId(), tenantDataSource.getDriverClass(), tenantDataSource.getUrl(), tenantDataSource.getUsername(), tenantDataSource.getPassword())) {
|
||||
CacheUtil.put(TENANT_DATASOURCE_CACHE, TENANT_DATASOURCE_KEY, tenantId, tenantDataSource, Boolean.FALSE);
|
||||
} else {
|
||||
tenantDataSource = null;
|
||||
}
|
||||
}
|
||||
return tenantDataSource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
|
||||
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.provider.AbstractJdbcDataSourceProvider;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
|
||||
import com.baomidou.dynamic.datasource.toolkit.DsStrUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tenant.utils.ShardingUtil;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.springblade.core.tenant.constant.TenantBaseConstant.*;
|
||||
|
||||
/**
|
||||
* 租户数据源初始加载
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantDataSourceJdbcProvider extends AbstractJdbcDataSourceProvider {
|
||||
|
||||
private final String driverClassName;
|
||||
private final String url;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final DefaultDataSourceCreator dataSourceCreator;
|
||||
private final DynamicDataSourceProperties dynamicDataSourceProperties;
|
||||
|
||||
public TenantDataSourceJdbcProvider(DefaultDataSourceCreator dataSourceCreator, DynamicDataSourceProperties dynamicDataSourceProperties, String driverClassName, String url, String username, String password) {
|
||||
super(dataSourceCreator, driverClassName, url, username, password);
|
||||
this.dataSourceCreator = dataSourceCreator;
|
||||
this.dynamicDataSourceProperties = dynamicDataSourceProperties;
|
||||
this.driverClassName = driverClassName;
|
||||
this.url = url;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, DataSourceProperty> executeStmt(Statement statement) throws SQLException {
|
||||
// 构建数据源集合
|
||||
Map<String, DataSourceProperty> map = new HashMap<>(16);
|
||||
// 构建主数据源
|
||||
DataSourceProperty masterProperty = new DataSourceProperty();
|
||||
masterProperty.setDriverClassName(driverClassName);
|
||||
masterProperty.setUrl(url);
|
||||
masterProperty.setUsername(username);
|
||||
masterProperty.setPassword(password);
|
||||
masterProperty.setDruid(dynamicDataSourceProperties.getDruid());
|
||||
map.put(dynamicDataSourceProperties.getPrimary(), masterProperty);
|
||||
// 构建yml数据源
|
||||
Map<String, DataSourceProperty> datasource = dynamicDataSourceProperties.getDatasource();
|
||||
if (!datasource.isEmpty()) {
|
||||
map.putAll(datasource);
|
||||
}
|
||||
// 构建动态数据源
|
||||
ResultSet rs = statement.executeQuery(TENANT_DATASOURCE_GROUP_STATEMENT);
|
||||
while (rs.next()) {
|
||||
int category = rs.getInt("category");
|
||||
String tenantId = rs.getString("tenantId");
|
||||
String driver = rs.getString("driverClass");
|
||||
String url = rs.getString("url");
|
||||
String username = rs.getString("username");
|
||||
String password = rs.getString("password");
|
||||
String shardingConfig = rs.getString("shardingConfig");
|
||||
// JDBC直连配置
|
||||
if (category == JDBC_CATEGORY && StringUtil.isNoneBlank(tenantId, driver, url, username, password)) {
|
||||
DataSourceProperty jdbcProperty = new DataSourceProperty();
|
||||
jdbcProperty.setDriverClassName(driver);
|
||||
jdbcProperty.setUrl(url);
|
||||
jdbcProperty.setUsername(username);
|
||||
jdbcProperty.setPassword(password);
|
||||
jdbcProperty.setDruid(dynamicDataSourceProperties.getDruid());
|
||||
map.put(tenantId, jdbcProperty);
|
||||
}
|
||||
// Sharding分库分表配置
|
||||
else if (category == SHARDING_CATEGORY && StringUtil.isNotBlank(shardingConfig)) {
|
||||
DataSource dataSource = ShardingUtil.createDataSource(shardingConfig);
|
||||
TenantDataSourceProperty shardingProperty = new TenantDataSourceProperty();
|
||||
shardingProperty.setTenantId(tenantId);
|
||||
shardingProperty.setDataSource(dataSource);
|
||||
map.put(tenantId, shardingProperty);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, DataSource> loadDataSources() {
|
||||
Connection conn = null;
|
||||
Statement stmt = null;
|
||||
try {
|
||||
// 由于 SPI 的支持,现在已无需显示加载驱动了
|
||||
// 但在用户显示配置的情况下,进行主动加载
|
||||
if (!DsStrUtils.isEmpty(driverClassName)) {
|
||||
Class.forName(driverClassName);
|
||||
log.info("成功加载数据库驱动程序");
|
||||
}
|
||||
conn = DriverManager.getConnection(url, username, password);
|
||||
log.info("成功获取数据库连接");
|
||||
stmt = conn.createStatement();
|
||||
Map<String, DataSourceProperty> dataSourcePropertiesMap = executeStmt(stmt);
|
||||
return createDataSourceMap(dataSourcePropertiesMap);
|
||||
} catch (Exception e) {
|
||||
log.error("获取数据库连接失败", e);
|
||||
} finally {
|
||||
closeResource(conn);
|
||||
closeResource(stmt);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭资源
|
||||
*
|
||||
* @param con 资源
|
||||
*/
|
||||
private static void closeResource(AutoCloseable con) {
|
||||
if (con != null) {
|
||||
try {
|
||||
con.close();
|
||||
} catch (SQLException ex) {
|
||||
log.debug("关闭连接失败", ex);
|
||||
} catch (Throwable ex) {
|
||||
log.debug("关闭连接时遇到异常", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, DataSource> createDataSourceMap(Map<String, DataSourceProperty> dataSourcePropertiesMap) {
|
||||
Map<String, DataSource> dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size() * 2);
|
||||
for (Map.Entry<String, DataSourceProperty> item : dataSourcePropertiesMap.entrySet()) {
|
||||
String dsName = item.getKey();
|
||||
DataSourceProperty dataSourceProperty = item.getValue();
|
||||
String poolName = dataSourceProperty.getPoolName();
|
||||
if (StringUtil.isBlank(poolName)) {
|
||||
poolName = dsName;
|
||||
}
|
||||
TenantDataSourceProperty tenantDataSourceProperty = BeanUtil.copyProperties(dataSourceProperty, TenantDataSourceProperty.class);
|
||||
DataSource dataSource = Objects.requireNonNull(tenantDataSourceProperty).getDataSource();
|
||||
if (dataSource == null) {
|
||||
dataSourceProperty.setPoolName(poolName);
|
||||
dataSourceMap.put(dsName, dataSourceCreator.createDataSource(dataSourceProperty));
|
||||
} else {
|
||||
dataSourceMap.put(dsName, dataSource);
|
||||
}
|
||||
}
|
||||
return dataSourceMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.dynamic;
|
||||
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* 租户数据源配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TenantDataSourceProperty extends DataSourceProperty {
|
||||
|
||||
/**
|
||||
* 租户id
|
||||
*/
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 自定义数据源
|
||||
*/
|
||||
private DataSource dataSource;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.processor;
|
||||
|
||||
import org.springblade.core.auto.annotation.AutoEnvPostProcessor;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* 初始化分库分表配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoEnvPostProcessor
|
||||
public class TenantEnvPostProcessor implements EnvironmentPostProcessor, Ordered {
|
||||
|
||||
private static final String DYNAMIC_DATASOURCE_KEY = "spring.datasource.dynamic.enabled";
|
||||
|
||||
private static final String AUTOCONFIGURE_EXCLUDE_KEY = "spring.autoconfigure.exclude";
|
||||
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
environment.getSystemProperties().put(DYNAMIC_DATASOURCE_KEY, "false");
|
||||
environment.getSystemProperties().put(AUTOCONFIGURE_EXCLUDE_KEY, "com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tenant.utils;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* ShardingUtil
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class ShardingUtil {
|
||||
|
||||
@SneakyThrows
|
||||
public static DataSource createDataSource(String yamlConfig) {
|
||||
return YamlShardingSphereDataSourceFactory.createDataSource(yamlConfig.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user