人人妻人人澡人人爽人人精品av_精品乱码一区内射人妻无码_老司机午夜福利视频_精品成品国色天香摄像头_99精品福利国产在线导航_野花社区在线观看视频_大地资源在线影视播放_东北高大肥胖丰满熟女_金门瓶马车内剧烈运动

首頁>國內(nèi) > 正文

SpringBoot讀寫分離組件開發(fā)詳解-當(dāng)前速讀

2023-07-07 09:18:11來源:實(shí)戰(zhàn)案例錦集

實(shí)現(xiàn)目標(biāo):一寫多讀,讀可以任意配置多個,默認(rèn)都是從寫庫中進(jìn)行操作,只有符合條件的方法(指定的目標(biāo)方法或者標(biāo)有指定注解的方法才會從讀庫中操作)。獨(dú)立打成一個jar包放入本地倉庫。


(資料圖)

實(shí)現(xiàn)原理:通過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:定義切點(diǎn),那些方法是需要攔截(從讀庫中操作)。

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 ;  }  }
數(shù)據(jù)源路由
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動態(tài)設(shè)置context的內(nèi)容值,0為從寫庫中操作,其它的都在讀庫中操作。

BaseSlaveLoad類為到底從那個讀庫中選取的一個算法類,默認(rèn)實(shí)現(xiàn)使用的是輪詢算法。

public interface BaseSlaveLoad {  int load() ;  }public abstract class AbstractSlaveLoad implements BaseSlaveLoad {  @Resource  protected List slaveDataSources ;  }

這里定義一個抽象類注入了讀庫列表,所有的實(shí)現(xiàn)類從該類中繼承即可。

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() ;}
數(shù)據(jù)源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();  }}

應(yīng)該切點(diǎn)需要動態(tài)配置,所以這里采用spring aop的方式來配置

@Beanpublic AspectJExpressionPointcutAdvisor logAdvisor(RWDataSourceProperties props, DataSourceHolder holder) {  AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor() ;  logger.info("執(zhí)行表達(dá)式:{}", 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的注解方法或類都會從讀庫中操作。

到此讀寫分離組件開發(fā)完成。

打包安裝到本地倉庫
mvn install -Dmaven.test.skip=true
新建base-web項(xiàng)目

引入依賴

  com.pack  xg-component-rw  1.0.0

啟動類添加注解開啟讀寫分離功能

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

測試:

第一次查詢:

圖片

第二次查詢:

圖片

為了區(qū)別兩個從庫設(shè)置不同的數(shù)據(jù)

這里是寫庫

關(guān)鍵詞:

相關(guān)新聞

Copyright 2015-2020   三好網(wǎng)  版權(quán)所有 聯(lián)系郵箱:435 22 [email protected]  備案號: 京ICP備2022022245號-21