上篇文章介绍了如何手工使用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 ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
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;
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());
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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
将该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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
配置文件包括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) {
}
或者
@Autowired
@Qualifier("ds1")
private DataSource dataSource1;
@Resource(name = "ds2")
private DataSource dataSource2;
本文共享的代码可以直接使用了,大家可以根据自己需要进行调整。
然而我们在项目中不一定需要直接使用dataSource的,大家都习惯使用JDBC的jdbcTemplate、Mybatis的sqlSessionTemplate,再或者就是以Mybatis为例直接动态代理到Mapper接口上。
那么如何做到完全动态数据源呢,以至于实现我们可以为同一个Java类的不同方法,分别指定使用不同的数据源?下篇文章将为大家揭晓。