This commit is contained in:
trang
2017-11-23 17:39:29 +08:00
parent 7c32107204
commit a7457d08d6
5 changed files with 134 additions and 63 deletions
@@ -97,10 +97,10 @@ spring:
druid:
# 继承 spring.datasource.druid 前缀的配置,同时可以自定义配置,若名称相同则覆盖父配置
data-sources:
master-data-source:
master:
max-active: 50
max-open-prepared-statements: 50
slave-data-source:
slave:
max-active: 25
max-open-prepared-statements: 25
@@ -56,23 +56,9 @@ public class NormalTest {
@Test
public void hashcode() {
Hash hash = new Hash();
Hash hash2 = new Hash();
System.out.println(hash == hash2);
System.out.println(hash.equals(hash2));
String camelAlias = "XxXDatasource";
String alias = camelAlias.substring(0, camelAlias.toLowerCase().indexOf("DataSource".toLowerCase()));
System.out.println(alias);
}
class Hash {
@Override
public int hashCode() {
return 1;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
}
@@ -9,9 +9,12 @@ import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.github.trang.druid.autoconfigure.datasource.DruidDataSource2;
import com.github.trang.druid.autoconfigure.properties.DruidDataSourceProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@@ -20,8 +23,14 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.github.trang.druid.autoconfigure.properties.DruidDataSourceProperties.*;
/**
@@ -101,4 +110,24 @@ public class DruidAutoConfiguration {
return new CommonsLogFilter();
}
@Bean
@ConditionalOnBean(DruidDataSource.class)
public Map<String, DruidDataSource> dataSourceMap(ConfigurableListableBeanFactory beanFactory) {
return new HashMap<>(beanFactory.getBeansOfType(DruidDataSource2.class));
}
/**
* 为了便于使用,增加数据源集合,可直接注入使用
* 如需使用 Set、Array 请注入 Set<DruidDataSource2> 或 DruidDataSource2[]
*
* @param dataSourceMap 多数据源
* @return List<DruidDataSource> dataSourceList
*/
@Bean
@ConditionalOnBean(name = "dataSourceMap")
@DependsOn("dataSourceMap")
public List<DruidDataSource> dataSourceList(Map<String, DruidDataSource> dataSourceMap) {
return new ArrayList<>(dataSourceMap.values());
}
}
@@ -1,22 +1,21 @@
package com.github.trang.druid.autoconfigure;
import com.github.trang.druid.autoconfigure.datasource.DruidDataSource2;
import com.github.trang.druid.autoconfigure.properties.DruidDataSourceProperties;
import com.github.trang.druid.autoconfigure.util.CharMatcher;
import com.alibaba.druid.pool.DruidDataSource;
import com.github.trang.druid.autoconfigure.DruidDataSourceConfiguration.DataSourceImportSelector;
import com.github.trang.druid.autoconfigure.datasource.DruidDataSource2;
import com.github.trang.druid.autoconfigure.util.CharMatcher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.bind.PropertySourcesBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.*;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
@@ -24,18 +23,25 @@ import org.springframework.core.type.AnnotationMetadata;
import java.util.Map;
/**
* Druid 数据源配置
*
* @author trang
*/
@Import({DataSourceImportSelector.class})
@Import(DataSourceImportSelector.class)
@Slf4j
public class DruidDataSourceConfiguration {
public static class SingleDataSourceRegister implements ImportBeanDefinitionRegistrar {
/**
* 单数据源注册
*
* @author trang
*/
static class SingleDataSourceRegister implements ImportBeanDefinitionRegistrar {
static final String BEAN_NAME = "dataSource";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BEAN_NAME)) {
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource2.class)
.setInitMethodName("init")
@@ -47,48 +53,62 @@ public class DruidDataSourceConfiguration {
}
}
public static class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, BeanFactoryAware, EnvironmentAware {
/**
* 多数据源注册
*
* @author trang
*/
static class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private DruidDataSourceProperties druidDataSourceProperties;
private DefaultListableBeanFactory beanFactory;
private ConfigurableEnvironment environment;
private static final String BEAN_SUFFIX = "DataSource";
private RelaxedPropertyResolver resolver;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
druidDataSourceProperties.getDataSources().forEach((dataSourceName, dataSourceConfig) -> {
// 构造 BeanDefinition,通过 DruidDataSource2 实现继承 'spring.datasource.druid' 的配置
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource2.class)
.setInitMethodName("init")
.setDestroyMethodName("close")
.getBeanDefinition();
// 注册 BeanDefinition
String beanName = CharMatcher.separatedToCamel().apply(dataSourceName);
registry.registerBeanDefinition(beanName, beanDefinition);
// 将 'spring.datasource.druid.data-sources.${name}' 的配置绑定到 Bean
DruidDataSource2 dataSource = beanFactory.getBean(beanName, DruidDataSource2.class);
PropertySourcesBinder propertySourcesBinder = new PropertySourcesBinder(environment);
propertySourcesBinder.bindTo("spring.datasource.druid.data-sources." + dataSourceName, dataSource);
log.debug("druid {}-data-source init...", dataSourceName);
});
}
@Autowired
public void setDruidDataSourceProperties(DruidDataSourceProperties properties) {
this.druidDataSourceProperties = properties;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
resolver.getSubProperties("").keySet().stream()
.map(key -> key.substring(0, key.indexOf(".")))
.distinct()
.forEach(dataSourceName -> {
// 构造 BeanDefinition,通过 DruidDataSource2 实现继承 'spring.datasource.druid' 的配置
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource2.class)
.setInitMethodName("init")
.setDestroyMethodName("close")
.getBeanDefinition();
// 注册 BeanDefinition
registry.registerBeanDefinition(dataSourceName, beanDefinition);
// 注册驼峰别名
String camelAlias = CharMatcher.separatedToCamel().apply(dataSourceName);
registry.registerAlias(dataSourceName, camelAlias);
// 注册以 DataSource 为后缀的别名
String otherAlias;
if (camelAlias.toLowerCase().endsWith(BEAN_SUFFIX.toLowerCase())) {
if (!camelAlias.endsWith(BEAN_SUFFIX)) {
otherAlias = camelAlias.substring(0, camelAlias.toLowerCase().indexOf(BEAN_SUFFIX.toLowerCase()));
otherAlias = otherAlias + BEAN_SUFFIX;
registry.registerAlias(dataSourceName, otherAlias);
}
} else {
otherAlias = camelAlias + BEAN_SUFFIX;
registry.registerAlias(dataSourceName, otherAlias);
}
log.debug("druid {}-data-source init...", dataSourceName);
});
}
@Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
this.resolver = new RelaxedPropertyResolver(environment, "spring.datasource.druid.data-sources.");
}
}
/**
* 数据源选择器
* 当配置文件中存在 spring.datasource.druid.data-sources 属性时为多数据源
* 不存在则为单数据源
*
* @author trang
*/
static class DataSourceImportSelector implements ImportSelector, EnvironmentAware {
private ConfigurableEnvironment environment;
@@ -98,9 +118,41 @@ public class DruidDataSourceConfiguration {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, "spring.datasource.druid.");
Map<String, Object> properties = resolver.getSubProperties("data-sources");
if (properties.isEmpty()) {
return new String[]{DruidDataSourceProperties.class.getName(), SingleDataSourceRegister.class.getName()};
return new String[]{SingleDataSourceRegister.class.getName()};
}
return new String[]{DruidDataSourceProperties.class.getName(), DynamicDataSourceRegister.class.getName()};
return new String[]{DynamicDataSourceRegister.class.getName(),
DataSourceBeanPostProcessor.class.getName()};
}
@Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}
}
/**
* DataSource Bean 处理器
*
* @author trang
*/
static class DataSourceBeanPostProcessor implements BeanPostProcessor, EnvironmentAware {
private ConfigurableEnvironment environment;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DruidDataSource) {
// 将 'spring.datasource.druid.data-sources.${name}' 的配置绑定到 Bean
PropertySourcesBinder binder = new PropertySourcesBinder(environment);
binder.bindTo("spring.datasource.druid.data-sources." + beanName, bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
@@ -18,6 +18,10 @@ public final class CharMatcher {
return Manipulation.SEPARATED_TO_CAMELCASE;
}
public static Manipulation camelToHyphen() {
return Manipulation.CAMELCASE_TO_HYPHEN;
}
public static enum Manipulation {
NONE {