Commit 6014e3de authored by ag's avatar ag

1. 读写分离组件提供.

2. 升级版本号为0.1.1
parent 6bb4a177
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>commons-parent</artifactId> <artifactId>commons-parent</artifactId>
<groupId>cn.quantgroup</groupId> <groupId>cn.quantgroup</groupId>
<version>0.1.0</version> <version>0.1.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>commons-parent</artifactId> <artifactId>commons-parent</artifactId>
<groupId>cn.quantgroup</groupId> <groupId>cn.quantgroup</groupId>
<version>0.1.0</version> <version>0.1.1</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -150,6 +150,21 @@ ...@@ -150,6 +150,21 @@
<artifactId>spring-cloud-starter-stream-kafka</artifactId> <artifactId>spring-cloud-starter-stream-kafka</artifactId>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!--<dependency>-->
<!--<groupId>javax.persistence</groupId>-->
<!--<artifactId>persistence-api</artifactId>-->
<!--<version>1.0</version>-->
<!--</dependency>-->
</dependencies> </dependencies>
......
package cn.quantgroup.tech.db;
/**
* 数据源类型. 主/从
* @author ag
*/
public enum DSType {
MASTER,
SLAVE,
}
package cn.quantgroup.tech.db;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author ag
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
package cn.quantgroup.tech.db;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledInfoContributor;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author ag
*/
@Aspect
@Order(-1)
@Slf4j
public class DynamicDataSourceAspect {
@Before("@annotation(ds)")
public void changeDataSource(JoinPoint point, TargetDataSource ds) {
DSType dsId = ds.type();
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
log.error("数据源[{}]不存在,使用默认数据源 > {}", ds.type(), point.getSignature());
return;
}
log.debug("Use DataSource : {} > {}", ds.type(), point.getSignature());
DynamicDataSourceContextHolder.setDataSourceType(ds.type());
}
@After("@annotation(ds)")
public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
log.debug("Revert DataSource : {} > {}", ds.type(), point.getSignature());
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
\ No newline at end of file
package cn.quantgroup.tech.db;
import org.springframework.context.annotation.Bean;
/**
* 加载切面
* @author ag
*/
public class DynamicDataSourceConfiguration {
@Bean
public DynamicDataSourceAspect dynamicDataSourceAspect(){
return new DynamicDataSourceAspect();
}
}
package cn.quantgroup.tech.db;
import java.util.ArrayList;
import java.util.List;
/**
* @author ag 额. 这是个....自己看吧
*/
class DynamicDataSourceContextHolder {
private static final ThreadLocal<DSType> CONTEXT_HOLDER = new ThreadLocal<>();
static List<DSType> dataSourceIds = new ArrayList<>();
static void setDataSourceType(DSType dataSourceType) {
CONTEXT_HOLDER.set(dataSourceType);
}
static DSType getDataSourceType() {
return CONTEXT_HOLDER.get();
}
static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
/**
* 判断指定DataSource当前是否存在
*/
static boolean containsDataSource(DSType dataSourceId) {
return dataSourceIds.contains(dataSourceId);
}
}
\ No newline at end of file
package cn.quantgroup.tech.db;
import lombok.extern.slf4j.Slf4j;
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.Configuration;
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;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 使用 EnableDynamicDataSource 注解会加载这个类
*
* @author ag
*/
@Slf4j
@Configuration
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private ConversionService conversionService = new DefaultConversionService();
/**
* 默认的数据库连接池类型
*/
private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
/**
* 默认的数据库驱动类型
*/
private static final String DATABASE_TYPE_DEFAULT = "com.mysql.jdbc.Driver";
private static final String MASTER_PREFIX = "spring.datasource.";
private static final String SLAVE_PREFIX = "slave.datasource.";
private DataSource masterDataSource;
private DataSource slaveDataSource;
@Override
public void setEnvironment(Environment environment) {
//1. 初始化配置. 2. 构建数据源. 3. 设置数据源属性
masterDataSource = buildDataSource(initDataSource(environment, MASTER_PREFIX));
dataBinder(masterDataSource, environment, MASTER_PREFIX);
slaveDataSource = buildDataSource(initDataSource(environment, SLAVE_PREFIX));
dataBinder(slaveDataSource, environment, SLAVE_PREFIX);
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
Map<Object, Object> targetDataSources = new HashMap<>(1);
// 添加主数据源
targetDataSources.put(DSType.MASTER, masterDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add(DSType.MASTER);
// 添加更多数据源
targetDataSources.put(DSType.SLAVE, slaveDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add(DSType.SLAVE);
// 创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
mpv.addPropertyValue("defaultTargetDataSource", masterDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
log.info("Dynamic DataSource Registry Success");
}
private Map<String, Object> initDataSource(Environment env, String prefix) {
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, prefix);
Map<String, Object> dsMap = new HashMap<>(1);
//类型
dsMap.put("type", propertyResolver.getProperty("type", DATASOURCE_TYPE_DEFAULT));
//默认也就是Mysql. 没其他的
dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name", DATABASE_TYPE_DEFAULT));
dsMap.put("url", propertyResolver.getProperty("url"));
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password"));
return dsMap;
}
/**
* 绑定属性
*/
private void dataBinder(DataSource dataSource, Environment env, String prefix) {
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
dataBinder.setConversionService(conversionService);
dataBinder.setIgnoreNestedProperties(false);
dataBinder.setIgnoreInvalidFields(false);
dataBinder.setIgnoreUnknownFields(true);
Map<String, Object> rpr = new RelaxedPropertyResolver(env, prefix).getSubProperties("");
Map<String, Object> values = new HashMap<>(rpr);
// 排除已经设置的属性
values.remove("type");
values.remove("driver-class-name");
values.remove("url");
values.remove("username");
values.remove("password");
PropertyValues dataSourcePropertyValues = new MutablePropertyValues(values);
dataBinder.bind(dataSourcePropertyValues);
}
/**
* 根据已知信息构造DataSource
*
* @param dsMap
* @return
*/
private DataSource buildDataSource(Map<String, Object> dsMap) {
try {
String type = dsMap.get("type").toString();
Class<? extends DataSource> dataSourceType = (Class<? extends DataSource>) Class.forName(type);
String driverClassName = dsMap.get("driver-class-name").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;
}
}
package cn.quantgroup.tech.db;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* 使用这个注解来开启动态数据源切换功能
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({DynamicDataSourceRegister.class, DynamicDataSourceConfiguration.class})
public @interface EnableDynamicDataSource {
}
\ No newline at end of file
package cn.quantgroup.tech.db;
import java.lang.annotation.*;
/**
* 在方法上使用,用于指定使用哪个数据源
* 类型有 主/从 两种
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
DSType type() default DSType.MASTER;
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>cn.quantgroup</groupId> <groupId>cn.quantgroup</groupId>
<artifactId>commons-parent</artifactId> <artifactId>commons-parent</artifactId>
<version>0.1.0</version> <version>0.1.1</version>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<commons-parent.version>0.1.0</commons-parent.version> <commons-parent.version>0.1.1</commons-parent.version>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<guava.version>23.0</guava.version> <guava.version>23.0</guava.version>
<apollo.client.version>0.10.0</apollo.client.version> <apollo.client.version>0.10.1</apollo.client.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.test.skip>true</maven.test.skip> <maven.test.skip>true</maven.test.skip>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment