首页>资讯>正文
SpringBoot读写分离组件开发详解
2023-07-07 09:25:11    来源:实战案例锦集

实现目标:一写多读,读可以任意配置多个,默认都是从写库中进行操作,只有符合条件的方法(指定的目标方法或者标有指定注解的方法才会从读库中操作)。独立打成一个jar包放入本地仓库。

实现原理:通过aop。

pom.xml配置文件
  org.springframework.boot  spring-boot-starter  org.springframework.boot  spring-boot-starter-data-jpa  org.springframework.boot  spring-boot-configuration-processor  true
application.yml配置文件
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 = new HashMap<>(2) ;    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@ConditionalOnMissingBeanpublic BaseSlaveLoad slaveLoad() {  return new PollingLoad() ;}  @Beanpublic DataSourceHolder dataSourceHolder() {  return new DataSourceHolder() ;}
数据源AOP
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的方式来配置

@Beanpublic 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 ;}
Enable开启功能
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
新建base-web项目

引入依赖

  com.pack  xg-component-rw  1.0.0

启动类添加注解开启读写分离功能

@SpringBootApplication@EnableRWpublic class BaseWebApplication {  public static void main(String[] args) {    SpringApplication.run(BaseWebApplication.class, args);  }}

测试:

第一次查询:

图片

第二次查询:

图片

为了区别两个从库设置不同的数据

这里是写库

关键词:

SpringBoot读写分离组件开发详解

实现目标:一写多读,读可以任意配置多个,默认都是从写库中进行操作,2023-07-07

携程与承德旅游集团签署战略协议 或打造国内知名文旅IP活动

双方将在多方面展开深度合作,包括打造国内知名文旅IP活动,丰富承德旅2023-07-07

推特指责 Meta 挖角员工创建 Threads

IT之家7月7日消息,Meta推出Threads不到24小时,注册用户数量已经超过32023-07-07

华泰证券:潮流崛起 深挖咖啡产业链机会

华泰证券研报指出,随着咖啡消费习惯结构化变迁,产品层级增加,能满足2023-07-07

五湖四海聚上饶,商用车供应链共商共建共享,助力产业智能制造快速健康发展

7月2-4日,“2023国际商用车智能制造暨车架技术峰会”成功举办。 " > 2023-07-07

创设“3+2+4”九大未来场景系统 火炉尖社区获评省级第三批未来社区

创新机制,建设共富基金,共建七彩社区共富基金,发起生活、活力、公益2023-07-07

科技动态:科普ipad mini4无法连接到app store怎么办以及ipad升级错误3194怎么解决

在这个正是日新月异的时代,飞黄腾达的时代,人类的科技、生活已经在近2023-07-07

新动力(300152.SZ)终止筹划通过发行股份及支付现金的方式收购德威华泰60%股权

智通财经APP讯,新动力(300152 SZ)公告,公司于2023年7月6日召开了第五2023-07-06

满满“黑科技” 记者探访大运村近距离体验智慧大运

【解说】智能翻译对讲系统83种语言助力无碍交流、智能无人“深夜食堂”2023-07-06

淮河能源控股集团摘牌苏布尔嘎井田项目

近日,淮河能源(600575)控股集团以72 97亿元成功摘牌内蒙古苏布尔嘎煤2023-07-06

劲嘉股份实控人乔鲁予被解除留置

劲嘉股份今日晚间公告称,公司今日收到于都县监察委员会解除留置通知书2023-07-06

兰州农商银行西果园支行:致力于打造人民群众认可的“乡村振兴主办行”

金融是经济发展的血脉,是乡村振兴的关键支撑节点。在金融活水不断注入2023-07-06

黄尖镇爱心企业夏日送清凉

黄尖镇爱心企业夏日送清凉2023-07-06

安卓手机怎么格式化u盘(安卓手机怎么格式化)

来为大家解答以上问题,安卓手机怎么格式化u盘,安卓手机怎么格式化很2023-07-06

飞鹤奶粉会导致宝宝早熟吗?飞鹤奶粉会不会太甜了?

飞鹤始建于1962年,一直专门针对中国人体质研制奶粉,对中国宝宝体质特2023-07-06

志在千里远,何惧前路难!山东泰山发布客战河南海报

记者季禹7月7日晚19:35,中超第15轮,山东泰山队即将客战河南。赛前,2023-07-06

环球观热点:第五届中国商业地产品牌价值论坛在京举行

中证网讯(记者董添)6月27日,第五届中国商业地产品牌价值论坛在北京2023-07-06

定襄中霍村出土的一批青铜器-焦点速讯

定襄中霍村出土的一批青铜器 郭艮堂 李培林(忻州市文物管理处) 19952023-07-06

逆水寒手游全职业技能内功搭配方法

还有很多逆水寒用户还不清楚逆水寒手游全职业技能内功搭配方法,下面就2023-07-06

鲅鱼的特点是什么_今日最新

鲅鱼是一种重要的经济鱼类,属于带鳍亚门,斑马鱼目。它主要生活在温带2023-07-06

硅料价格下降传导至下游 光伏组件小厂价格创2021年以来最低纪录_前沿热点

近日,保碧新能源光伏组件集采、水发兴业组价集采开标,总计近40家企业2023-07-06

环球观热点:走向“新”文旅之路

新乡日报全媒体记者宗斌琚铭洁夏日正盛,绿水淙淙,太行山水在牧野大地2023-07-06

市民建议上海天文馆放宽购票次数限制,上海市科委回应_当前速递

上海天文馆。人民视觉资料图有市民反映,上海天文馆限制同一证件号每月2023-07-06

当前快看:6月亚洲制造业PMI为50.4% 保持平稳增长态势

7月6日消息,2023年6月份全球主要国家制造业PMI公布,亚洲制造业采购经2023-07-06

滚动
资讯