zoukankan      html  css  js  c++  java
  • 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层调用 。

  • 相关阅读:
    ajax翻页效果模仿yii框架
    一个伪ajax图片上传代码的例子
    php下intval()和(int)转换有哪些区别
    php中iconv函数使用方法
    php字符串截取问题
    ASP.net UrlRewrite的防盗链功能
    ASP.NET中application对象
    javascript回车完美实现tab切换功能
    有关c#装箱和拆箱知识整理
    PHP四大安全策略
  • 原文地址:https://www.cnblogs.com/jay-wu/p/12030374.html
Copyright © 2011-2022 走看看