获取请求头的network-code进行动态生成数据源,切换数据源来实现数据的隔离。
package com.yukong.chapter5.aop;
import com.yukong.chapter5.annotation.DataSource;
import com.yukong.chapter5.config.DynamicDataSourceContextHolder;
import com.yukong.chapter5.config.DynamicRoutingDataSource;
import com.yukong.chapter5.properties.SaasDataSourceProperties;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@Aspect
@Component
@Order(-1900)
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Autowired
private SaasDataSourceProperties saasDataSourceProperties;
/**
* 参数绑定工具 springboot2.0新推出
*/
private Binder binder;
@Before("@annotation(ds)")
public void changeDataSource(JoinPoint point, DataSource ds) throws Throwable {
String dsId = ds.value();
if (DynamicDataSourceContextHolder.dataSourceIds.contains(dsId)) {
logger.debug("Use DataSource :{} >", dsId, point.getSignature());
DynamicDataSourceContextHolder.setDataSourceRouterKey(dsId);
} else {
logger.info("数据源[{}]不存在,使用默认数据源 >{}", dsId, point.getSignature());
String netWorkCode = ds.netWorkCode();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String header = request.getHeader("network-code");
//创建数据源
Map<String, javax.sql.DataSource> customDataSources = new HashMap<String, javax.sql.DataSource>();
saasDataSourceProperties.getSaas().entrySet().stream().forEach(a->{
Properties properties = new Properties();
properties.setProperty("driverClassName",a.getValue().getDriver_class_name());
String url = a.getValue().getUrl();
String template = a.getValue().getTemplate();
String replace = template.replace("network-code", "");
String newUrl = url.replace(replace, replace + "_" + netWorkCode);
properties.setProperty("url",newUrl);
properties.setProperty("username",a.getValue().getUsername());
UnpooledDataSourceFactory unpooledDataSourceFactory = new UnpooledDataSourceFactory();
unpooledDataSourceFactory.setProperties(properties);
javax.sql.DataSource dataSource = unpooledDataSourceFactory.getDataSource();
customDataSources.put(dsId,dataSource);
// bean定义类
GenericBeanDefinition define = new GenericBeanDefinition();
// 设置bean的类型,此处DynamicRoutingDataSource是继承AbstractRoutingDataSource的实现类
define.setBeanClass(DynamicRoutingDataSource.class);
// 需要注入的参数
MutablePropertyValues mpv = define.getPropertyValues();
// 添加默认数据源,避免key不存在的情况没有数据源可用
// 添加其他数据源
mpv.add("targetDataSources", dataSource);
DynamicDataSourceContextHolder.dataSourceIds.add(dsId);
logger.info("添加数据源"+newUrl);
});
}
}
@After("@annotation(ds)")
public void restoreDataSource(JoinPoint point, DataSource ds) {
logger.debug("Revert DataSource : " + ds.value() + " > " + point.getSignature());
DynamicDataSourceContextHolder.removeDataSourceRouterKey();
}
}
package com.yukong.chapter5.properties;
import lombok.Data;
@Data
public class DataSoureceObject {
private String password;
private String url;
private String driver_class_name;
private String username;
private String type;
private String template;
}
package com.yukong.chapter5.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Data
@Configuration
@ConfigurationProperties("spring.datasource.saas")
public class SaasDataSourceProperties {
private Map<String,DataSoureceObject> saas;
}