spring读写分离 - 事务注解篇

来源:互联网 发布:json格式怎么打开mac 编辑:程序博客网 时间:2024/05/17 07:53


思路参照 spring读写分离 - 事务配置篇(转) ,不过是基于@Transactional判断,所以每个需要事务的方法上都必须添加上这个注解,这里直接贴出代码:

配置文件:

多数据源配置:

  <bean id="dataSource"    class="com.lmiky.platform.database.datasource.DynamicDataSource">    <property name="readDataSources">      <list>        <ref bean="readDataSource1" />        <ref bean="readDataSource2" />      </list>    </property>    <property name="writeDataSource" ref="writeDataSource" />  </bean>

数据源拦截器:

  <bean id="dateSourceAspect"    class="com.lmiky.platform.database.datasource.DateSourceAspect" />  <aop:config expose-proxy="true">    <aop:aspect ref="dateSourceAspect" order="0">      <aop:pointcut id="dateSourcePointcut"        expression="execution(* com.lmiky..service.impl..*.*(..))"/>      <aop:around pointcut-ref="dateSourcePointcut" method="determineReadOrWriteDB" />    </aop:aspect>  </aop:config>

要保证让这个拦截在事务的拦截器之前,否则如果spring先拦截事务的话,就不会起效了。可以用order设的值来排序,或者把这个配置的代码放在跟事务配置的代码同一个页面,并且放在事务配置代码的前面,spring是按代码顺序来执行的。

其他的跟单个数据源配置一样。

java代码:


数据源拦截器
package com.lmiky.platform.database.datasource;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.transaction.annotation.Transactional;import java.lang.reflect.Method;import java.util.concurrent.ConcurrentHashMap;/** * 数据源切片 * * @author lmiky * @date 2015年9月7日 下午3:25:54 */public class DateSourceAspect {    /**     * 缓存     */    private static ConcurrentHashMap<String, Boolean> methodIsReadCache = new ConcurrentHashMap<>();    /**     * 决策是否只读     *     * @param pjp 织入点     * @return 方法执行结果     * @throws Throwable     * @author lmiky     * @date 2015年9月7日 下午3:45:27     */    public Object determineReadOrWriteDB(ProceedingJoinPoint pjp) throws Throwable {        Method method = ((MethodSignature) pjp.getSignature()).getMethod();        Object target = pjp.getTarget();        String cacheKey = target.getClass().getName() + "." + method.getName();        Boolean isReadCacheValue = methodIsReadCache.get(cacheKey);        if (isReadCacheValue == null) {            // 重新获取方法,否则传递的是接口的方法信息            Method realMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());            isReadCacheValue = isChoiceReadDB(realMethod);            methodIsReadCache.put(cacheKey, isReadCacheValue);        }        if (isReadCacheValue) {            DynamicDataSourceHolder.markRead();        } else {            DynamicDataSourceHolder.markWrite();        }        try {            return pjp.proceed();        } finally {            DynamicDataSourceHolder.reset();        }    }    /**     * 判断是否只读方法     *     * @param method 执行方法     * @return 当前方法是否只读     * @author lmiky     * @date 2015年9月7日 下午3:45:10     */    private boolean isChoiceReadDB(Method method) {        Transactional transactionalAnno = AnnotationUtils.findAnnotation(method, Transactional.class);        if (transactionalAnno == null) {            return true;        }        // 如果之前选择了写库,则现在还选择写库        if (DynamicDataSourceHolder.isChoiceWrite()) {            return false;        }        if (transactionalAnno.readOnly()) {            return true;        }        return false;    }}
数据源选择:

package com.lmiky.platform.database.datasource;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.concurrent.atomic.AtomicInteger;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * 动态数据源 * * @author lmiky * @date 2015年9月7日 下午2:01:31 */public class DynamicDataSource extends AbstractRoutingDataSource {    private Object writeDataSource;    private List<Object> readDataSources;    private int readDataSourceSize = 0;    private AtomicInteger readIndex = new AtomicInteger(0);    /**     * 数据源键名     */    private static final String DATASOURCE_KEY_WRITE = "write";    private static final String DATASOURCE_KEY_READ = "read";    /* (non-Javadoc)     * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#afterPropertiesSet()     */    @Override    public void afterPropertiesSet() {        if (this.writeDataSource == null) {            throw new IllegalArgumentException("Property 'writeDataSource' is required");        }        setDefaultTargetDataSource(writeDataSource);        Map<Object, Object> targetDataSources = new HashMap<>();        targetDataSources.put(DATASOURCE_KEY_WRITE, writeDataSource);        if (this.readDataSources == null) {            readDataSourceSize = 0;        } else {            for(int i=0; i<readDataSources.size(); i++) {                targetDataSources.put(DATASOURCE_KEY_READ + i, readDataSources.get(i));            }            readDataSourceSize = readDataSources.size();        }        setTargetDataSources(targetDataSources);        super.afterPropertiesSet();    }    /*     * (non-Javadoc)     * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()     */    @Override    protected Object determineCurrentLookupKey() {        if(DynamicDataSourceHolder.isChoiceNone() || DynamicDataSourceHolder.isChoiceWrite() || readDataSourceSize == 0) {            return DATASOURCE_KEY_WRITE;        }        int index = readIndex.incrementAndGet() % readDataSourceSize;        return DATASOURCE_KEY_READ + index;    }    /**     * @return the writeDataSource     */    public Object getWriteDataSource() {        return writeDataSource;    }    /**     * @param writeDataSource the writeDataSource to set     */    public void setWriteDataSource(Object writeDataSource) {        this.writeDataSource = writeDataSource;    }    /**     * @return the readDataSources     */    public List<Object> getReadDataSources() {        return readDataSources;    }    /**     * @param readDataSources the readDataSources to set     */    public void setReadDataSources(List<Object> readDataSources) {        this.readDataSources = readDataSources;    }}

package com.lmiky.platform.database.datasource;/** * 数据源管理器 * * @author lmiky * @date 2015年9月7日 下午2:02:23 */public class DynamicDataSourceHolder {    private static enum DataSourceType {        write, read;    }    public static final ThreadLocal<DataSourceType> holder = new ThreadLocal<>();    /**     * 数据源名称     */    public static final String DATASOURCE_WRITE = "write";    public static final String DATASOURCE_READ = "read";        /**     * 标记为写数据源     * @author lmiky     * @date 2015年9月9日 下午8:57:43     */    public static void markWrite() {        holder.set(DataSourceType.write);    }        /**     * 标记为读数据源     * @author lmiky     * @date 2015年9月9日 下午8:57:43     */    public static void markRead() {        holder.set(DataSourceType.read);    }        /**     * 重置     * @author lmiky     * @date 2015年9月9日 下午8:58:01     */    public static void reset() {        holder.set(null);    }        /**     * 是否还未设置数据源     * @author lmiky     * @date 2015年9月9日 下午8:58:09     * @return     */    public static boolean isChoiceNone() {        return null == holder.get();     }        /**     * 当前是否选择了写数据源     * @author lmiky     * @date 2015年9月9日 下午8:58:19     * @return     */    public static boolean isChoiceWrite() {        return DataSourceType.write == holder.get();    }        /**     * 当前是否选择了读数据源     * @author lmiky     * @date 2015年9月9日 下午8:58:29     * @return     */    public static boolean isChoiceRead() {        return DataSourceType.read == holder.get();    }}



0 0
原创粉丝点击