时间:2021-07-01 10:21:17 帮助过:12人阅读
Spring的RoutingDataSource
/** * 使用Spring的动态数据源,需要实现AbstractRoutingDataSource * 通过determineCurrentLookupKey方法拿到识别key来判断选择读/写数据源 * token显然是多例的, 所以引入ThreadLocal保存 */ public class DynamicDataSource extends AbstractRoutingDataSource { // 读库总数 private Integer slaveCount; // 读库轮询计数, 初始为-1, 本类为单例, AtomicInteger线程安全 private AtomicInteger counter = new AtomicInteger(-1); // 存储读库的识别key sl1ve01, slave02... 写库识别key为master private List<Object> slaveDataSources = new ArrayList<Object>(); //当前线程的写库/读库token private static final ThreadLocal<Boolean> tokenHolder = new ThreadLocal<>(); public static void markMaster(boolean isMaster){ tokenHolder.set(isMaster); } @Override protected Object determineCurrentLookupKey() { if (tokenHolder.get()) { return "master"; // 写库 } // 轮询读库, 得到的下标为:0、1、2... Integer index = counter.incrementAndGet() % slaveCount; if (counter.get() > 99999) { // 以免超出Integer范围 counter.set(-1); } return slaveDataSources.get(index); } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); // 父类的resolvedDataSources属性是private, 需要使用反射获取 Field field = ReflectionUtils.findField(AbstractRoutingDataSource.class, "resolvedDataSources"); field.setAccessible(true); // 设置可访问 try { Map<Object, DataSource> resolvedDataSources = (Map<Object, DataSource>) field.get(this); // 读库数等于dataSource总数减写库数 this.slaveCount = resolvedDataSources.size() - 1; for (Map.Entry<Object, DataSource> entry : resolvedDataSources.entrySet()) { if ("master".equals(entry.getKey())) { continue; } slaveDataSources.add(entry.getKey()); } } catch (Exception e) { e.printStackTrace(); } } }
spring配置文件
<!-- 定义事务策略 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--所有以query开头的方法都是只读的 --> <tx:method name="query*" read-only="true" /> <!-- readonly属性 --> <!--其他方法使用默认事务策略 --> <tx:method name="*" /> </tx:attributes> </tx:advice> <!-- 定义AOP切面处理器 --> <bean class="com.zx.DataSourceAspect" id="dataSourceAspect"> <!-- 注入事务策略 --> <property name="txAdvice" ref="txAdvice"/> <!-- 指定slave方法的前缀(非必须) --> <property name="slaveMethodStartWith" value="query,find,get"/> </bean> <aop:config> <aop:pointcut id="myPointcut" expression="execution(* com.zx.service.*.*(..))" /> <!-- 将切面应用到自定义的切面处理器上,-9999保证该切面优先级最高执行 --> <aop:aspect ref="dataSourceAspect" order="-9999"> <aop:before method="before" pointcut-ref="myPointcut" /> </aop:aspect> </aop:config> <!-- 定义数据源,继承了spring的动态数据源 --> <bean id="dataSource" class="com.zx.DynamicDataSource"> <!-- 设置多个数据源 --> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- 这些设置的key和determineCurrentLookupKey方法拿到的key相比对, 根据匹配选择数据源 --> <entry key="master" value-ref="masterDataSource"/> <!-- value-ref指向数据源 --> <entry key="slave01" value-ref="slave01DataSource"/> <entry key="slave02" value-ref="slave02DataSource"/> <entry key="slave03" value-ref="slave03DataSource"/> </map> </property> <!-- 设置默认的数据源,这里默认走写库 --> <property name="defaultTargetDataSource" ref="masterDataSource"/> </bean>
使用Spring实现MySQL读写分离
标签:this aop spring 自定义 处理 解决 java 需要 row