springboot多数据源

一 简介

     在以前的博客中,我介绍了怎么使用spring在项目中进行多数据源的切换,这里是在前一篇文章的基础上介绍如何使用springboot配置多数据源。为了方便理解,请先了解上一篇文章,spring多数据源传送门:点击打开链接。

二 区别和共同点

    区别: spring和springboot在多数据源上区别在于前者是在xml中进行的数据源配置,后者则是通过一个注册类来实现多数据源的注册。

   共同点:两者都是通过切面加注解的方式去切换数据源。

   当然springboot也提供了更简单的方法去实现数据源的切换,只是不够灵活,只能在包上面进行切换,不能够在类和方法上进行切换。所以这里才会继续沿用切面加注解的方式。

三 上代码

  注意事项:下面的代码是在spring多数据源的基础上写的,所以要想使用,先把之前的代码考到项目里,除了xml。如需完整的代码请加群 499950895  从群文件中下载myProject.rar

第一步

   在application.yml文件中添加数据源配置

spring:
  datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: root
      password: root
      initialize: true
custom:
   datasource:
      names: ds1,ds2
      ds1:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&useSSL=false          
        username: root
        password: root
      ds2:  
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test3?useUnicode=true&characterEncoding=UTF-8&useSSL=false          
        username: root
        password: root

在yml文件中必须包含一个主库,使用spring.datasource配置,从库则使用custom.datasource配置,names的值是从数据源的名称,多个用逗号隔开

第二步 多数据源注册

   
package com.gcx.api.common.dataSource;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

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.autoconfigure.jdbc.DataSourceBuilder;
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;
/**
 *<p>Title:DynamicDataSourceRegister</p>
 *<p>Description:动态数据源注册</p>
 *<p>Company:gcx</p>
 *<p>Author:zhanglin</p>
 *<p>Date:2018年3月27日</p>
 */
public class DynamicDataSourceRegister implements
		ImportBeanDefinitionRegistrar, EnvironmentAware {
	
    //如配置文件中未指定数据源类型,使用该默认值

    private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";

    private ConversionService conversionService = new DefaultConversionService();

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

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

	public DynamicDataSourceRegister() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void setEnvironment(Environment environment) {
		  initDefaultDataSource(environment);
	      initCustomDataSources(environment);
	}

	private void initCustomDataSources(Environment env) {
        // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");

        String dsPrefixs = propertyResolver.getProperty("names");
        String dsn="";
        for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
        	dsn=dsPrefix+".";
            Map<String, Object> dsMap = new HashMap<String, Object>();
            dsMap.put("type", propertyResolver.getProperty(dsn+"type"));
            dsMap.put("driverClassName", propertyResolver.getProperty(dsn+"driverClassName"));
            dsMap.put("url", propertyResolver.getProperty(dsn+"url"));
            dsMap.put("username", propertyResolver.getProperty(dsn+"username"));
            dsMap.put("password", propertyResolver.getProperty(dsn+"password"));
            
            DataSource ds = buildDataSource(dsMap);

            customDataSources.put(dsPrefix, ds);

            dataBinder(ds, env);

        }
		
	}
    /**
     * 加载主数据源配置
     * @param env
     */
	private void initDefaultDataSource(Environment env) {
		//读取主数据源
		RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
		Map<String, Object> dsMap = new HashMap<String, Object>();
	    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, env);
	}

	private void dataBinder(DataSource dataSource, Environment env) {
	        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);

	        dataBinder.setConversionService(conversionService);

	        dataBinder.setIgnoreNestedProperties(false);//false

	        dataBinder.setIgnoreInvalidFields(false);//false

	        dataBinder.setIgnoreUnknownFields(true);//true

	        if(dataSourcePropertyValues == null){

	            Map<String, Object> rpr = new RelaxedPropertyResolver(env, "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);
		
	}

	@SuppressWarnings("unchecked")
	private DataSource buildDataSource(Map<String, Object> dsMap) {
		
		Object type = dsMap.get("type");
        if (type == null){ //默认数据源
            type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
        }

        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();

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

             return factory.build();

        } catch (ClassNotFoundException e) {

            e.printStackTrace();

        }
		return null;
	}

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata,
			BeanDefinitionRegistry registry) {
		
	        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
	        // 将主数据源添加到更多数据源中
	        targetDataSources.put("dataSource", defaultDataSource);
	        // 添加更多数据源
	        targetDataSources.putAll(customDataSources);
	        // 创建DynamicDataSource
	        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();

	        beanDefinition.setBeanClass(DynamicDataSource.class);

	        beanDefinition.setSynthetic(true);

	        MutablePropertyValues mpv = beanDefinition.getPropertyValues();

	        //添加属性:AbstractRoutingDataSource.defaultTargetDataSource
	        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);

	        mpv.addPropertyValue("targetDataSources", targetDataSources);

	        registry.registerBeanDefinition("dataSource", beanDefinition);

	}

}

EnvironmentAware接口使用来读取application.yml配置文件的,可以通过重写setEnvironment方法来获取配置文件的内容。

ImportBeanDefinitionRegistrar接口定义了registerBeanDefinitions方法,从而允许我们向Spring注册必要的Bean,该方法主要是通过BeanDefinitionRegistry参数spring IOC中动态的装配bean

GenericBeanDefinition是自2.5以后新加入的bean文件配置属性定义类,是一站式服务类。除了具有指定类、可选的构造参数值和属性参数这些其它bean definition一样的特性外,它还具有通过parenetName属性来灵活设置parent bean definition。

通常, GenericBeanDefinition用来注册用户可见的bean definition(可见的bean definition意味着可以在该类bean definition上定义post-processor来对bean进行操作,甚至为配置parent name做扩展准


第三 在启动类上加上@Import注解

@SpringBootApplication
@Import({DynamicDataSourceRegister.class})
@MapperScan("com.gcx.api.dao")
public class Application extends SpringBootServletInitializer{
	
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@Import注解在4.2,支持导入普通的java类,并将其声明成一个bean。

至此便可以在方法或类上使用注解去切换数据源。

如果不想理解原理,只想快速的使用此功能测只需要把项目中的dataSource包考在项目中,然后在启动类上加上@Import({DynamicDataSourceRegister.class})即可使用

包名如下图


注意DataSourceName中的属性值是yml配置中的从数据源名称







 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值