Spring+Hibernate+MySQL的主从分离/读写分离问题(2)

来源:互联网 发布:js解除绑定click事件 编辑:程序博客网 时间:2024/04/28 15:01

这里上具体代码,因为没时间做单独的可运行实例,所以这里只贴关键的代码。

DataSource的配置

<bean id="parentDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">        <property name="defaultAutoCommit" value="${hibernate.defaultAutoCommit}" />        <property name="maxTotal" value="${hibernate.maxTotal}" />        <property name="maxIdle" value="${hibernate.maxIdle}" />        <property name="initialSize" value="${hibernate.initialSize}" />        <property name="testOnBorrow" value="${hibernate.testOnBorrow}" />        <property name="validationQuery" value="${hibernate.validationQuery}" />        <property name="connectionInitSqls" value="set names utf8mb4" />    </bean>    <bean id="masterDataSource" class="org.apache.commons.dbcp2.BasicDataSource"  parent="parentDataSource">        <property name="driverClassName" value="${jdbc.master.driver}" />        <property name="url" value="${jdbc.master.url}" />        <property name="username" value="${jdbc.master.username}" />        <property name="password" value="${jdbc.master.password}" />    </bean>    <bean id="slaveDataSource" class="org.apache.commons.dbcp2.BasicDataSource" parent="parentDataSource">        <property name="driverClassName" value="${jdbc.slave.driver}" />        <property name="url" value="${jdbc.slave.url}" />        <property name="username" value="${jdbc.slave.username}" />        <property name="password" value="${jdbc.slave.password}" />    </bean>

ReplicationRoutingDataSource为AbstractRoutingDataSource的实现

<bean id="dataSource" class="com.xxx.xxx.ReplicationRoutingDataSource">            <property name="targetDataSources">                <map key-type="java.lang.String">                   <entry key="MASTER" value-ref="masterDataSource"/>                   <entry key="SLAVE" value-ref="slaveDataSource"/>                </map>            </property>            <property name="defaultTargetDataSource" ref="masterDataSource"/>       </bean>

ReplicationRoutingDataSource 写法

public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {    private Map<Object, Object> tempTargetDataSources = null;    public ReplicationRoutingDataSource(){        DynamicDataSourceAspect.replicationRoutingDataSource = this;    }    @Override    public void setTargetDataSources(Map<Object, Object> targetDataSources) {        super.setTargetDataSources(targetDataSources);        tempTargetDataSources = targetDataSources;    }    public Map<Object, Object> getTargetDataSources() {        return tempTargetDataSources;    }    @Override    protected Object determineCurrentLookupKey() {        return DynamicDataSourceContextHolder.getDataSourceType();    }}

DataSource的动态管理类通过ThreadLocal保证线程安全

public class DynamicDataSourceContextHolder {    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();    public static List<String> dataSourceIds = new ArrayList<>();    public static void setDataSourceType(String dataSourceType) {        contextHolder.set(dataSourceType);    }    public static String getDataSourceType() {        return contextHolder.get();    }    public static void clearDataSourceType() {        contextHolder.remove();    }    /**     * 判断指定DataSrouce当前是否存在     *     * @param dataSourceId     * @return     * @author zxl     * @create       */    public static boolean containsDataSource(String dataSourceId){        return dataSourceIds.contains(dataSourceId);    }}

自定义注解

@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface TargetDataSource {    String name();}

切面类,通过自定义注解TargetDataSource作为切入点

public class DynamicDataSourceAspect {    private Logger logger = Logger.getLogger(DynamicDataSourceAspect.class);    public static ReplicationRoutingDataSource replicationRoutingDataSource = null;    public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {        String dsId = ds.name();        if (replicationRoutingDataSource!=null                &&!replicationRoutingDataSource.getTargetDataSources().containsKey(dsId)) {            logger.error("DataSource not exist "+ds.name() + point.getSignature());        } else {            logger.debug("Use DataSource : {} > {}"+ds.name()+ point.getSignature());            DynamicDataSourceContextHolder.setDataSourceType(ds.name());        }    }    public void restoreDataSource(JoinPoint point, TargetDataSource ds) {        logger.debug("Revert DataSource : {} > {}"+ds.name()+ point.getSignature());        DynamicDataSourceContextHolder.clearDataSourceType();    }}

AOP配置,这里请注意,由于配置到Contorller层的缘故,如果发现配置无效,请检查是否出现AOP配置冲突

<!-- 指定DataSourceAOP,用于动态切换DataSource -->    <bean id="dataSourceAspect" class="com.xxx.xxx.DynamicDataSourceAspect" />    <aop:config>        <aop:aspect id="aspectDataSourceConfig" ref="dataSourceAspect" >            <aop:before method="changeDataSource" pointcut="@annotation(ds)"/>            <aop:after method="restoreDataSource" pointcut="@annotation(ds)"/>        </aop:aspect>    </aop:config>

使用方法

@Controller@RequestMapping(value = "/test")public class TestController extends BaseController {    @RequestMapping(value = "/testmaster", method = {RequestMethod.POST, RequestMethod.GET })    @ResponseBody    @TargetDataSource(name="SLAVE")    public String onTestSlave(HttpServletRequest request, HttpServletResponse response) {        //具体业务        return "result";    }    @RequestMapping(value = "/testslave", method = {RequestMethod.POST, RequestMethod.GET })    @ResponseBody    @TargetDataSource(name="MASTER")    public String onTestMaster(HttpServletRequest request, HttpServletResponse response) {        //具体业务        return "result";    }}

参考
1. http://blog.csdn.net/caomiao2006/article/details/38989789
2. http://stackoverflow.com/questions/30023704/database-routing-using-abstractroutingdatasource

0 0
原创粉丝点击