吴文杰的个人github博客

spring boot 多数据源加载原理

git代码:https://gitee.com/wwj912790488/multiple-data-sources

 

DynamicDataSourceAspect切面 必须定义@Order(-10),保证该aop在@Transaction之前执行

 

 

 

配置如下,分别加载三个数据库配置

 

 

 

1.利用ImportBeanDefinitionRegistrar和EnvironmentAware 加载注册多个数据源bean
package org.spring.boot.multiple.ds;

import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author donghongchen
 * @create 2017-09-04 15:34
 * <p>
 * 动态数据源注册
 **/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    //如果配置文件中未指定数据源类型,使用默认值
    private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";

    private ConversionService conversionService = new DefaultConversionService();

    private PropertyValues dataSourcePropertyValues;
    //默认数据源
    private DataSource defaultDataSource;

    private Map<String, DataSource> customDataSources = new HashMap<>();

    /**
     * 加载多数据源配置
     *
     * @param environment
     */
    @Override
    public void setEnvironment(Environment environment) {
        initDefaultDataSource(environment);
        initCustomDataSources(environment);
    }

    private void initDefaultDataSource(Environment environment) {
        //读取主数据源
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "spring.datasource.");
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("type", propertyResolver.getProperty("type"));
        dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
        dsMap.put("url", propertyResolver.getProperty("url"));
        dsMap.put("username", propertyResolver.getProperty("username"));
        dsMap.put("password", propertyResolver.getProperty("password"));
        //创建数据源
        defaultDataSource = buildDataSource(dsMap);
        dataBinder(defaultDataSource, environment);
    }


    private void initCustomDataSources(Environment environment) {
        //读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "custom.datasource.");
        String dsPrefixs = propertyResolver.getProperty("names");
        if (null == dsPrefixs || "".equals(dsPrefixs)) {
            return;
        }
        String[] dsPrefixsArr = dsPrefixs.split(",");
        for (String dsPrefix : dsPrefixsArr) {
            Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
            DataSource ds = buildDataSource(dsMap);
            customDataSources.put(dsPrefix, ds);
            dataBinder(ds, environment);
        }
    }

    private DataSource buildDataSource(Map<String, Object> dsMap) {
        Object type = dsMap.get("type");
        if (type == null) {
            type = DATASOURCE_TYPE_DEFAULT;
        }
        Class<? extends DataSource> dataSourceType;
        try {
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
            String driverClassName = dsMap.get("driverClassName").toString();
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();

            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            dataSource.setDriverClassName(driverClassName);

            return dataSource;

//            DataSourceBuilder factory = DataSourceBuilder.create().
//                    driverClassName(driverClassName).type(dataSourceType).url(url).username(username).password(password);
//            return factory.build();

        } catch (ClassNotFoundException ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }

    private void dataBinder(DataSource dataSource, Environment environment) {
        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
        dataBinder.setConversionService(conversionService);
        dataBinder.setIgnoreNestedProperties(false);
        dataBinder.setIgnoreInvalidFields(false);
        dataBinder.setIgnoreUnknownFields(true);
        if (dataSourcePropertyValues == null) {
            Map<String, Object> rpr = new RelaxedPropertyResolver(environment, "spring.datasource").
                    getSubProperties(".");
            Map<String, Object> values = new HashMap<>(rpr);
            //排除已经设置的属性
            values.remove("type");
            values.remove("driverClassName");
            values.remove("url");
            values.remove("username");
            values.remove("password");
            dataSourcePropertyValues = new MutablePropertyValues(values);
        }
        dataBinder.bind(dataSourcePropertyValues);
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> targetDataSource = new HashMap<>();
        //将主数据源添加到更多数据源中
        targetDataSource.put("dataSource", defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIDS.add("dataSource");

        //添加更多数据源
        targetDataSource.putAll(customDataSources);
        for (String key : customDataSources.keySet()) {
            DynamicDataSourceContextHolder.dataSourceIDS.add(key);
        }
        //创建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        //添加属性
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSource);
        registry.registerBeanDefinition("dataSource", beanDefinition);

    }


}

 

根据@annotation 去动态切换数据源

package org.spring.boot.multiple.ds;

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.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author donghongchen
 * @create 2017-09-04 14:44
 * <p>
 * 切换数据源Advice
 **/
@Aspect
@Order(-10) //保证该aop在@Transaction之前执行
@Component
public class DynamicDataSourceAspect {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * * @Before("@annotation(ds)")
     * 的意思是:@Before:在方法执行之前进行执行; @annotation(targetDataSource):会拦截注解targetDataSource的方法,否则不拦截;
     * @param point
     * @param targetDataSource
     */
    @Before("@annotation(targetDataSource)")
    public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource){
        //获取当前的指定数据源
        String dsID = targetDataSource.value();
        //如果不在我们注入的所有的数据源范围内,输出警告信息,系统自动使用默认的数据源
        if (!DynamicDataSourceContextHolder.containsDataSource(dsID)){
            logger.error("数据源["+dsID+"]不存在,使用默认的数据源 > { " + dsID+", 方法签名:"+point.getSignature()+"}");
        }else {
            logger.info("Use DataSource:   {" +dsID+", 方法签名:"+point.getSignature() +"}");
            //找到的话,那么设置动态数据源上下文
            DynamicDataSourceContextHolder.setDataSourceType(dsID);
        }
    }


    @After("@annotation(targetDataSource)")
    public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource){
        //方法执行完毕后,销毁当前数据源信息,进行垃圾回收
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

最后对应到对应的DAO层调用 。

posted @ 2019-12-12 17:12  我是2货呀  阅读(893)  评论(0编辑  收藏  举报
Live2D