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配置中的從數據源名稱







 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章