Spring Boot 多數據源
來自: http://blog.csdn.net/catoop/article/details/50564513
上篇文章介紹了如何手工使用Java代碼將對象注冊到Spring中,為本文“多數據源”做了基礎。
下面一個Java類是我已經寫好的根據配置文件動態創建多dataSource的代碼,其原理也很簡單,就是讀取配置文件,根據配置文件中配置的數據源數量,動態創建dataSource并注冊到Spring中。
代碼如下:
package org.springboot.sample.config;import java.util.HashMap; import java.util.Map; import java.util.Map.Entry;
import javax.sql.DataSource;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.AnnotationScopeMetadataResolver; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ScopeMetadata; import org.springframework.context.annotation.ScopeMetadataResolver; import org.springframework.core.env.Environment;
/* 動態創建多數據源注冊到Spring中 @author 單紅宇(365384722) @myblog http://blog.csdn.net/catoop/ @create 2016年1月22日 */ @Configuration public class MultipleDataSourceBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
private static final Logger logger = LoggerFactory .getLogger(MultipleDataSourceBeanDefinitionRegistryPostProcessor.class); // 如配置文件中未指定數據源類型,使用該默認值 private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
// private static final Object DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); // 存放DataSource配置的集合,模型<dataSourceName,dataSourceMap> private Map<String, Map<String, Object>> dataSourceMap = new HashMap<>(); @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { logger.info("Invoke Metho postProcessBeanFactory"); beanFactory.getBeanDefinition("dataSource").setPrimary(true); BeanDefinition bd = null; Map<String, Object> dsMap = null; for (Entry<String, Map<String, Object>> entry : dataSourceMap.entrySet()) { bd = beanFactory.getBeanDefinition(entry.getKey()); MutablePropertyValues mpv = bd.getPropertyValues(); dsMap = entry.getValue(); mpv.addPropertyValue("driverClassName", dsMap.get("url")); mpv.addPropertyValue("url", dsMap.get("url")); mpv.addPropertyValue("username", dsMap.get("username")); mpv.addPropertyValue("password", dsMap.get("password")); } } @SuppressWarnings("unchecked") @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { logger.info("Invoke Metho postProcessBeanDefinitionRegistry"); try { if(!dataSourceMap.isEmpty()){ for (Entry<String, Map<String, Object>> entry : dataSourceMap.entrySet()) { Object type = entry.getValue().get("type"); if(type == null) type = DATASOURCE_TYPE_DEFAULT;// 默認DataSource registerBean(registry, entry.getKey(), (Class<? extends DataSource>)Class.forName(type.toString())); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 注冊Bean到Spring * * @param registry * @param name * @param beanClass * @author SHANHY * @create 2016年1月22日 */ private void registerBean(BeanDefinitionRegistry registry, String name, Class<?> beanClass) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); // 可以自動生成name String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); } /** * 加載多數據源配置 */ @Override public void setEnvironment(Environment env) { RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource."); String dsPrefixs = propertyResolver.getProperty("names"); for (String dsPrefix : dsPrefixs.split(",")) {// 多個數據源 Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + "."); dataSourceMap.put(dsPrefix, dsMap); } }
}</pre>
將該Java文件直接添加到項目中便可,無其他任何代碼耦合,就是單純一個類。
再來看一下在配置文件中配置多數據源的方法:
# 主數據源,默認的 spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=123456更多數據源
custom.datasource.names=ds1,ds2,ds3 custom.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test custom.datasource.ds1.username=root custom.datasource.ds1.password=123456
custom.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test custom.datasource.ds2.username=root custom.datasource.ds2.password=123456
custom.datasource.ds3.type=com.zaxxer.hikari.HikariDataSource custom.datasource.ds3.driver-class-name=com.mysql.jdbc.Driver custom.datasource.ds3.url=jdbc:mysql://localhost:3306/test custom.datasource.ds3.username=root custom.datasource.ds3.password=123456
下面為連接池的補充設置,應用到上面所有數據源中
spring.datasource.maximum-pool-size=100 spring.datasource.max-idle=10 spring.datasource.max-wait=10000 spring.datasource.min-idle=5 spring.datasource.initial-size=5 spring.datasource.validation-query=SELECT 1 spring.datasource.test-on-borrow=false spring.datasource.test-while-idle=true spring.datasource.time-between-eviction-runs-millis=18800</pre>
配置文件包括1個主數據源和多個數據源,其中主數據源在Spring中的beanName默認為dataSource,另外幾個數據源的beanName分包為:ds1、ds2、ds3,大家看一下配置的規則,想必不用多說。
其中datasource的type屬性可以具體指定到我們需要的數據源上面,不指定情況下默認為:org.apache.tomcat.jdbc.pool.DataSource當然你也可以把這些數據源配置到主dataSource數據庫中,然后讀取數據庫生成多數據源。當然這樣做的必要性并不大,難不成數據源還會經常變嗎。
在需要應用dataSource的地方需要指定名稱,如:
// 方法參數注入方式 public void testDataSource(@Qualifier("ds1") DataSource myDataSource, @Qualifier("dataSource") DataSource dataSource) {}</pre>
或者
// 類成員屬性注入方式 @Autowired @Qualifier("ds1") private DataSource dataSource1;@Resource(name = "ds2") private DataSource dataSource2;</pre>
本文共享的代碼可以直接使用了,大家可以根據自己需要進行調整。
然而我們在項目中不一定需要直接使用dataSource的,大家都習慣使用JDBC的jdbcTemplate、Mybatis的sqlSessionTemplate,再或者就是以Mybatis為例直接動態代理到Mapper接口上。
那么如何做到完全動態數據源呢,以至于實現我們可以為同一個Java類的不同方法,分別指定使用不同的數據源?下篇文章將為大家揭曉。
</div>