springboot+mybatis实现数据隔离(saas)

获取请求头的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 customDataSources = new HashMap();
            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  saas;
}

你可能感兴趣的:(springboot)