环境:springboot2.2.6RELEASE

创新互联是专业的老城网站建设公司,老城接单;提供成都网站设计、网站制作,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行老城网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!
实现目标:一写多读,读可以任意配置多个,默认都是从写库中进行操作,只有符合条件的方法(指定的目标方法或者标有指定注解的方法才会从读库中操作)。独立打成一个jar包放入本地仓库。
实现原理:通过aop。
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-configuration-processor true 
- pack:
 - datasource:
 - pointcut: execution(public * net.greatsoft.service.base.*.*(..)) || execution(public * net.greatsoft.service.xxx.*.*(..))
 - master:
 - driverClassName: oracle.jdbc.driver.OracleDriver
 - jdbcUrl: jdbc:oracle:thin:@10.100.102.113:1521/orcl
 - username: test
 - password: test
 - minimumIdle: 10
 - maximumPoolSize: 200
 - autoCommit: true
 - idleTimeout: 30000
 - poolName: MbookHikariCP
 - maxLifetime: 1800000
 - connectionTimeout: 30000
 - connectionTestQuery: SELECT 1 FROM DUAL
 - slaves:
 - - driverClassName: oracle.jdbc.driver.OracleDriver
 - jdbcUrl: jdbc:oracle:thin:@10.100.102.113:1521/orcl
 - username: dc
 - password: dc
 - minimumIdle: 10
 - maximumPoolSize: 200
 - autoCommit: true
 - idleTimeout: 30000
 - poolName: MbookHikariCP
 - maxLifetime: 1800000
 - connectionTimeout: 30000
 - connectionTestQuery: SELECT 1 FROM DUAL
 - - driverClassName: oracle.jdbc.driver.OracleDriver
 - jdbcUrl: jdbc:oracle:thin:@10.100.102.113:1521/orcl
 - username: empi
 - password: empi
 - minimumIdle: 10
 - maximumPoolSize: 200
 - autoCommit: true
 - idleTimeout: 30000
 - poolName: MbookHikariCP
 - maxLifetime: 1800000
 - connectionTimeout: 30000
 - connectionTestQuery: SELECT 1 FROM DUAL
 
pointcut:定义切点,那些方法是需要拦截(从读库中操作)。
master:写库配置。
slaves:读库配置(List集合)。
- @Component
 - @ConfigurationProperties(prefix = "pack.datasource")
 - public class RWDataSourceProperties {
 - private String pointcut ;
 - private HikariConfig master ;
 - private List
 slaves = new ArrayList<>(); - }
 
- public class RWConfig {
 - private static Logger logger = LoggerFactory.getLogger(RWConfig.class) ;
 - @Bean
 - public HikariDataSource masterDataSource(RWDataSourceProperties rwDataSourceProperties) {
 - return new HikariDataSource(rwDataSourceProperties.getMaster()) ;
 - }
 - @Bean
 - public List
 slaveDataSources(RWDataSourceProperties rwDataSourceProperties) { - List
 lists = new ArrayList<>() ; - for(HikariConfig config : rwDataSourceProperties.getSlaves()) {
 - lists.add(new HikariDataSource(config)) ;
 - }
 - return lists ;
 - }
 - @Bean
 - @Primary
 - @DependsOn({"masterDataSource", "slaveDataSources"})
 - public AbstractRoutingDataSource routingDataSource(@Qualifier("masterDataSource")DataSource masterDataSource,
 - @Qualifier("slaveDataSources")List
 slaveDataSources) { - BaseRoutingDataSource ds = new BaseRoutingDataSource() ;
 - Map
 - targetDataSources.put("master", masterDataSource) ;
 - for (int i = 0; i < slaveDataSources.size(); i++) {
 - targetDataSources.put("slave-" + i, slaveDataSources.get(i)) ;
 - }
 - ds.setDefaultTargetDataSource(masterDataSource) ;
 - ds.setTargetDataSources(targetDataSources) ;
 - return ds ;
 - }
 - }
 
- public class BaseRoutingDataSource extends AbstractRoutingDataSource {
 - @Resource
 - private DataSourceHolder holder;
 - @Override
 - protected Object determineCurrentLookupKey() {
 - return holder.get() ;
 - }
 - }
 
- public class DataSourceHolder {
 - private ThreadLocal
 context = new ThreadLocal () { - @Override
 - protected Integer initialValue() {
 - return 0 ;
 - }
 - };
 - @Resource
 - private BaseSlaveLoad slaveLoad ;
 - public String get() {
 - Integer type = context.get() ;
 - return type == null || type == 0 ? "master" : "slave-" + slaveLoad.load() ;
 - }
 - public void set(Integer type) {
 - context.set(type) ;
 - }
 - }
 
通过aop动态设置context的内容值,0为从写库中操作,其它的都在读库中操作。
BaseSlaveLoad类为到底从那个读库中选取的一个算法类,默认实现使用的是轮询算法。
- public interface BaseSlaveLoad {
 - int load() ;
 - }
 
- public abstract class AbstractSlaveLoad implements BaseSlaveLoad {
 - @Resource
 - protected List
 slaveDataSources ; - }
 
这里定义一个抽象类注入了读库列表,所有的实现类从该类中继承即可。
- public class PollingLoad extends AbstractSlaveLoad {
 - private int index = 0 ;
 - private int size = 1 ;
 - @PostConstruct
 - public void init() {
 - size = slaveDataSources.size() ;
 - }
 - @Override
 - public int load() {
 - int n = index ;
 - synchronized (this) {
 - index = (++index) % size ;
 - }
 - return n ;
 - }
 - }
 
配置成Bean
- @Bean
 - @ConditionalOnMissingBean
 - public BaseSlaveLoad slaveLoad() {
 - return new PollingLoad() ;
 - }
 - @Bean
 - public DataSourceHolder dataSourceHolder() {
 - return new DataSourceHolder() ;
 - }
 
- public class DataSourceAspect implements MethodInterceptor {
 - private DataSourceHolder holder ;
 - public DataSourceAspect(DataSourceHolder holder) {
 - this.holder = holder ;
 - }
 - @Override
 - public Object invoke(MethodInvocation invocation) throws Throwable {
 - Method method = invocation.getMethod() ;
 - String methodName = method.getName() ;
 - SlaveDB slaveDB = method.getAnnotation(SlaveDB.class) ;
 - if (slaveDB == null) {
 - slaveDB = method.getDeclaringClass().getAnnotation(SlaveDB.class) ;
 - }
 - if (methodName.startsWith("find")
 - || methodName.startsWith("get")
 - || methodName.startsWith("query")
 - || methodName.startsWith("select")
 - || methodName.startsWith("list")
 - || slaveDB != null) {
 - holder.set(1) ;
 - } else {
 - holder.set(0) ;
 - }
 - return invocation.proceed();
 - }
 - }
 
应该切点需要动态配置,所以这里采用spring aop的方式来配置
- @Bean
 - public AspectJExpressionPointcutAdvisor logAdvisor(RWDataSourceProperties props, DataSourceHolder holder) {
 - AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor() ;
 - logger.info("执行表达式:{}", props.getPointcut()) ;
 - advisor.setExpression(props.getPointcut()) ;
 - advisor.setAdvice(new DataSourceAspect(holder)) ;
 - return advisor ;
 - }
 
- public class RWImportSelector implements ImportSelector {
 - @Override
 - public String[] selectImports(AnnotationMetadata importingClassMetadata) {
 - return new String[] {RWConfig.class.getName()} ;
 - }
 - }
 
这里的RWConfig为我们上面的配置类
- @Retention(RetentionPolicy.RUNTIME)
 - @Target(ElementType.TYPE)
 - @Documented
 - @Import({RWImportSelector.class})
 - public @interface EnableRW {
 - }
 
- @Documented
 - @Retention(RUNTIME)
 - @Target({ TYPE, METHOD })
 - public @interface SlaveDB {
 - }
 
有@SlaveDB的注解方法会类都会从读库中操作。
到此读写分离组件开发完成。
- mvn install -Dmaven.test.skip=true
 
引入依赖
com.pack xg-component-rw 1.0.0 
启动类添加注解开启读写分离功能
- @SpringBootApplication
 - @EnableRW
 - public class BaseWebApplication {
 - public static void main(String[] args) {
 - SpringApplication.run(BaseWebApplication.class, args);
 - }
 - }
 
测试:
第一次查询:
第二次查询:
为了区别两个从库设置不同的数据
这里是写库
完毕!!!
                文章题目:SpringBoot读写分离组件开发详解
                
                文章路径:http://www.csdahua.cn/qtweb/news36/497736.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网