diff --git a/pom.xml b/pom.xml index cafc4ecfb..67699fb41 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,11 @@ org.springframework.boot spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-aop + mysql mysql-connector-java @@ -194,6 +199,13 @@ taobao-sdk-java-auto 1502853394064-20180710 + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + diff --git a/src/main/java/au/com/royalpay/payment/manage/PaymentManageApplication.java b/src/main/java/au/com/royalpay/payment/manage/PaymentManageApplication.java index f6b97e01f..d2ce602a1 100644 --- a/src/main/java/au/com/royalpay/payment/manage/PaymentManageApplication.java +++ b/src/main/java/au/com/royalpay/payment/manage/PaymentManageApplication.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.core.io.Resource; @@ -22,9 +23,13 @@ import java.util.Properties; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -@SpringBootApplication(scanBasePackages = {"au.com.royalpay.payment", "cn.yixblog"}) +/** + * exclude = {DataSourceAutoConfiguration.class} + * 禁用springboot默认加载的application.properties单数据源配置 + */ +@SpringBootApplication(scanBasePackages = {"au.com.royalpay.payment", "cn.yixblog"},exclude = {DataSourceAutoConfiguration.class}) @EnableScheduling -@EnableCaching +@EnableCaching( proxyTargetClass=true ) public class PaymentManageApplication { public static void main(String[] args) { diff --git a/src/main/java/au/com/royalpay/payment/manage/pos/datasource/DataSourceConfiguration.java b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/DataSourceConfiguration.java new file mode 100644 index 000000000..f50c714a1 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/DataSourceConfiguration.java @@ -0,0 +1,35 @@ +package au.com.royalpay.payment.manage.pos.datasource; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; + +/** + * 多数据源配置类 + * Created by Dulingling on 2019/8/7 + */ +@Configuration +@EnableTransactionManagement +public class DataSourceConfiguration { + @Value("${spring.datasource.type}") + private Class dataSourceType; + + @Bean(name = "masterDataSource") + @Primary + @ConfigurationProperties(prefix = "spring.datasource.master") + public DataSource masterDataSource(){ + return DataSourceBuilder.create().type(dataSourceType).build(); + } + + @Bean(name = "slaveDataSource") + @ConfigurationProperties(prefix = "spring.datasource.slave") + public DataSource slaveDataSource1(){ + return DataSourceBuilder.create().type(dataSourceType).build(); + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/pos/datasource/DbContextHolder.java b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/DbContextHolder.java new file mode 100644 index 000000000..b00ca25fc --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/DbContextHolder.java @@ -0,0 +1,28 @@ +package au.com.royalpay.payment.manage.pos.datasource; + +/** + * Created by Dulingling on 2019/8/7 + */ +public class DbContextHolder { + + public enum DbType{ + MASTER,SLAVE + } + + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static void setDbType(DbType dbType){ + if(dbType==null)throw new NullPointerException(); + contextHolder.set(dbType); + } + + public static DbType getDbType(){ + DbType dbType = contextHolder.get(); + return dbType ==null? DbType.MASTER: dbType; + } + + public static void clearDbType(){ + contextHolder.remove(); + } + +} diff --git a/src/main/java/au/com/royalpay/payment/manage/pos/datasource/MybatisConfiguration.java b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/MybatisConfiguration.java new file mode 100644 index 000000000..b329a44b4 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/MybatisConfiguration.java @@ -0,0 +1,63 @@ +package au.com.royalpay.payment.manage.pos.datasource; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ibatis.mapping.DatabaseIdProvider; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.session.SqlSessionFactory; +import org.aspectj.apache.bcel.util.ClassLoaderRepository; +import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer; +import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration; +import org.mybatis.spring.boot.autoconfigure.MybatisProperties; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.annotation.Resource; +import javax.sql.DataSource; +import java.util.List; +import java.util.Map; + +/** + * Created by Dulingling on 2019/8/7 + */ +@Configuration +@AutoConfigureAfter({DataSourceConfiguration.class}) +public class MybatisConfiguration extends MybatisAutoConfiguration { + + + private static Log logger = LogFactory.getLog(MybatisConfiguration.class); + + @Resource(name = "masterDataSource") + private DataSource masterDataSource; + @Resource(name = "slaveDataSource") + private DataSource slaveDataSource; + + public MybatisConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider databaseIdProvider, ObjectProvider> configurationCustomizersProvider) { + super(properties, interceptorsProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider); + } + + @Bean + public SqlSessionFactory sqlSessionFactory() throws Exception { + return super.sqlSessionFactory(roundRobinDataSouceProxy()); + } + + public AbstractRoutingDataSource roundRobinDataSouceProxy(){ + ReadWriteSplitRoutingDataSource proxy = new ReadWriteSplitRoutingDataSource(); + Map targetDataResources = new ClassLoaderRepository.SoftHashMap(); + targetDataResources.put(DbContextHolder.DbType.MASTER,masterDataSource); + targetDataResources.put(DbContextHolder.DbType.SLAVE,slaveDataSource); + proxy.setDefaultTargetDataSource(masterDataSource);//默认源 + proxy.setTargetDataSources(targetDataResources); + return proxy; + } + + public void test(){ + + } +} + diff --git a/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadOnlyConnection.java b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadOnlyConnection.java new file mode 100644 index 000000000..d05f66489 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadOnlyConnection.java @@ -0,0 +1,15 @@ +package au.com.royalpay.payment.manage.pos.datasource; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 该注解注释在service方法上,标注为链接slaves库 + * Created by Dulingling on 2019/8/7 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ReadOnlyConnection { +} diff --git a/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadOnlyConnectionInterceptor.java b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadOnlyConnectionInterceptor.java new file mode 100644 index 000000000..f67f48b4c --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadOnlyConnectionInterceptor.java @@ -0,0 +1,40 @@ +package au.com.royalpay.payment.manage.pos.datasource; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; + + +/** + * Created by Dulingling on 2019/8/7 + */ + +@Aspect +@Component +public class ReadOnlyConnectionInterceptor implements Ordered { + + public static final Logger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class); + + @Around("@annotation(readOnlyConnection)") + public Object proceed(ProceedingJoinPoint proceedingJoinPoint,ReadOnlyConnection readOnlyConnection) throws Throwable { + try { + logger.info("set database connection to read only"); + DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE); + Object result = proceedingJoinPoint.proceed(); + return result; + }finally { + DbContextHolder.clearDbType(); + logger.info("restore database connection"); + } + } + + + @Override + public int getOrder() { + return 0; + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadWriteSplitRoutingDataSource.java b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadWriteSplitRoutingDataSource.java new file mode 100644 index 000000000..8f54862d9 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/pos/datasource/ReadWriteSplitRoutingDataSource.java @@ -0,0 +1,20 @@ +package au.com.royalpay.payment.manage.pos.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * Created by Dulingling on 2019/8/7 + */ +public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource { + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Override + public Object determineCurrentLookupKey() { + DbContextHolder.DbType dataSource = DbContextHolder.getDbType(); + logger.error("------------------当前数据源:{}---------------------",dataSource); + return dataSource; + } + +}