This commit is contained in:
trang
2017-11-23 17:46:09 +08:00
3 changed files with 303 additions and 1 deletions
@@ -1,7 +1,7 @@
package com.github.trang.druid.actuate.autoconfigure;
import com.github.trang.druid.actuate.DruidDataSourceEndpoint;
import com.github.trang.druid.actuate.DruidDataSourceMvcEndpoint;
import com.github.trang.druid.actuate.DruidDataSourceEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
@@ -0,0 +1,165 @@
package com.github.trang.druid.autoconfigure;
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.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.boot.bind.PropertySourcesBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
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;
import java.util.Map;
/**
* Druid 数据源配置
*
* @author trang
*/
@Import(DataSourceImportSelector.class)
@Slf4j
public class DruidDataSourceConfiguration {
/**
* 单数据源注册
*
* @author trang
*/
static class SingleDataSourceRegister implements ImportBeanDefinitionRegistrar {
static final String BEAN_NAME = "dataSource";
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BEAN_NAME)) {
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource2.class)
.setInitMethodName("init")
.setDestroyMethodName("close")
.getBeanDefinition();
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
log.debug("druid data-source init...");
}
}
}
/**
* 多数据源注册
*
* @author trang
*/
static class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final String BEAN_SUFFIX = "DataSource";
private RelaxedPropertyResolver resolver;
@Override
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.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;
@Override
public String[] selectImports(AnnotationMetadata metadata) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, "spring.datasource.druid.");
Map<String, Object> properties = resolver.getSubProperties("data-sources");
if (properties.isEmpty()) {
return new String[]{SingleDataSourceRegister.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
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}
}
}
@@ -0,0 +1,137 @@
package com.github.trang.druid.autoconfigure.util;
import org.springframework.util.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author trang
*/
public final class CharMatcher {
public static Manipulation none() {
return Manipulation.NONE;
}
public static Manipulation separatedToCamel() {
return Manipulation.SEPARATED_TO_CAMELCASE;
}
public static Manipulation camelToHyphen() {
return Manipulation.CAMELCASE_TO_HYPHEN;
}
public static enum Manipulation {
NONE {
@Override
public String apply(String value) {
return value;
}
},
HYPHEN_TO_UNDERSCORE {
@Override
public String apply(String value) {
return value.indexOf('-') != -1 ? value.replace('-', '_') : value;
}
},
UNDERSCORE_TO_PERIOD {
@Override
public String apply(String value) {
return value.indexOf('_') != -1 ? value.replace('_', '.') : value;
}
},
PERIOD_TO_UNDERSCORE {
@Override
public String apply(String value) {
return value.indexOf('.') != -1 ? value.replace('.', '_') : value;
}
},
CAMELCASE_TO_UNDERSCORE {
@Override
public String apply(String value) {
if (value.isEmpty()) {
return value;
}
Matcher matcher = CAMEL_CASE_PATTERN.matcher(value);
if (!matcher.find()) {
return value;
}
matcher = matcher.reset();
StringBuffer result = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(result, matcher.group(1) + '_' + StringUtils.uncapitalize(matcher.group(2)));
}
matcher.appendTail(result);
return result.toString();
}
},
CAMELCASE_TO_HYPHEN {
@Override
public String apply(String value) {
if (value.isEmpty()) {
return value;
}
Matcher matcher = CAMEL_CASE_PATTERN.matcher(value);
if (!matcher.find()) {
return value;
}
matcher = matcher.reset();
StringBuffer result = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(result, matcher.group(1) + '-'
+ StringUtils.uncapitalize(matcher.group(2)));
}
matcher.appendTail(result);
return result.toString();
}
},
SEPARATED_TO_CAMELCASE {
@Override
public String apply(String value) {
return separatedToCamelCase(value, false);
}
},
CASE_INSENSITIVE_SEPARATED_TO_CAMELCASE {
@Override
public String apply(String value) {
return separatedToCamelCase(value, true);
}
};
private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("([^A-Z-])([A-Z])");
private static final Pattern SEPARATED_TO_CAMEL_CASE_PATTERN = Pattern.compile("[_\\-.]");
private static final char[] SUFFIXES = new char[]{'_', '-', '.'};
public abstract String apply(String value);
private static String separatedToCamelCase(String value, boolean caseInsensitive) {
if (value.isEmpty()) {
return value;
}
StringBuilder builder = new StringBuilder();
for (String field : SEPARATED_TO_CAMEL_CASE_PATTERN.split(value)) {
field = (caseInsensitive ? field.toLowerCase() : field);
builder.append(builder.length() == 0 ? field : StringUtils.capitalize(field));
}
char lastChar = value.charAt(value.length() - 1);
for (char suffix : SUFFIXES) {
if (lastChar == suffix) {
builder.append(suffix);
break;
}
}
return builder.toString();
}
}
}