前段时间刚换了家新公司,然后看项目代码里用了数据库读写分离的架构,然后好奇扒了代码简单看了下,总体来说就是运用spring aop切面方式来实现的。看明白后就在自己的个人小项目里运用了下,测试OK,所以下面总结下流程:

  1、首先定义一个数据源注解,它有两个值,一个对应写库(主库),一个对应读库(从库)

package com.jdd.ds;

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    String DATA_SOURCE_READ = "dataSourceRead";
    String DATA_SOURCE_WRITE = "dataSourceWrite";

    String name() default "dataSourceWrite";
}

  2、在需要拦截的方法上加上该注解

package com.jdd.service.impl;

import com.jdd.dao.UserDao;
import com.jdd.ds.DataSource;
import com.jdd.pojo.User;
import com.jdd.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao;

    @DataSource(name=DataSource.DATA_SOURCE_READ)
    @Override
    public User getUserByNameAndPassword(String name, String password) {
        return userDao.getUserByNameAndPassword(name, password);
    }

    @DataSource(name=DataSource.DATA_SOURCE_WRITE)
    @Override
    public int insertUser(User user) {
        return userDao.insertUser(user);
    }
}

  3、然后在配置文件里加上aop切面配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- 扫描包加载Service实现类 -->
    <context:component-scan base-package="com.jdd.service"></context:component-scan>

    <bean id="dataSourceExchange" class="com.jdd.ds.DataSourceExchange"></bean>
    <aop:config>
        <aop:aspect ref="dataSourceExchange">
            <aop:around method="execute" pointcut="within(com.jdd.service.impl.*)"></aop:around>
        </aop:aspect>
    </aop:config>

</beans>

  4、然后我们要在切面类里,获取到方法上的dataSource注解里设置的值,从而决定使用哪个数据源

package com.jdd.ds;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

public class DataSourceExchange {

    private static Logger logger = LoggerFactory.getLogger(DataSourceExchange.class);

    public Object execute(ProceedingJoinPoint pjp){

        logger.info("DataSourceExchange==>");
        Object obj = null;

        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        Method targetMethod = methodSignature.getMethod();
        Method realMethod = null;
        try {
            realMethod = pjp.getTarget().getClass().getDeclaredMethod(signature.getName(), targetMethod.getParameterTypes());
            if(realMethod.isAnnotationPresent(DataSource.class)){
                DataSource datasource = (DataSource) realMethod.getAnnotation(DataSource.class);
                DataSourceContext.setDbType(datasource.name());
                logger.info(realMethod.getName() +" set dbtype==>"+datasource.name());
            }else{
                DataSourceContext.setDbType("dataSourceWrite");
            }
            obj = pjp.proceed();
        } catch (Throwable t) {
            t.printStackTrace();
        }
        logger.info(realMethod.getName()+" clear datatype==>");
        DataSourceContext.clearDbType();
        return obj;

    }

}

  在方法执行前,设置具体数据源,然后方法执行完后,再清除掉该值。

  注:注意上面的通过反射获取业务方法的代码, 开始的时候用

  MethodSignature signature = (MethodSignature)pjp.getSignature();

  Method method = signature.getMethod();

  发现通过这种方法获取的method,它是没有注解信息的。后来在网上搜了下,大概说是这种方法获取的是代理方法,不是目标方法。代理方法是不带注解信息的,所以这块注意下。

  5、下面看下 DataSourceContext 类,作用自然就是设置数据源上下文。

package com.jdd.ds;

public class DataSourceContext {

    public static final String DATA_SOURCE_READ = "dataSourceRead";
    public static final String DATA_SOURCE_WRITE = "dataSourceWrite";
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public DataSourceContext(){
    }

    public static void setDbType(String dbType){
        contextHolder.set(dbType);
    }

    public static String getDbType(){
        return (String)contextHolder.get();
    }

    public static void clearDbType(){
        contextHolder.remove();
    }

}

  6、下面定义 MultipleDataSource类, 继承与spring的 AbstractRoutingDataSource类, 并重写它的 determineCurrentLookupKey 方法, 作用就是从上下文获取当前线程使用的数据源标识:

package com.jdd.ds;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class MultipleDataSource extends AbstractRoutingDataSource{

    @Override
    protected Object determineCurrentLookupKey() {

        Object key = DataSourceContext.getDbType();
        if(key != null){
            this.logger.info("当前线程使用的数据源标识为 [ " + key.toString() + " ].");
        }
        return key;
    }
}

  7、最后在 配置文件 applicationContext-mysql.xml 里 配上数据源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- 加载配置文件 -->
    <context:property-placeholder location="classpath:resource/*.properties" />

    <!-- 数据库连接池 -->
    <bean id="dataSourceWrite" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="${url.write}" />
        <property name="username" value="${username.write}" />
        <property name="password" value="${password.write}" />
        <property name="maxActive" value="10" />
        <property name="minIdle" value="5" />
    </bean>

    <bean id="dataSourceRead" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="${url.read}" />
        <property name="username" value="${username.read}" />
        <property name="password" value="${password.read}" />
        <property name="maxActive" value="10" />
        <property name="minIdle" value="5" />
    </bean>
    <bean id="multipleDataSource" class="com.jdd.ds.MultipleDataSource">
        <property name="defaultTargetDataSource" ref="dataSourceWrite" />
        <property name="targetDataSources">
            <map>
                <entry value-ref="dataSourceWrite" key="dataSourceWrite"></entry>
                <entry value-ref="dataSourceRead" key="dataSourceRead"></entry>
            </map>
        </property>
    </bean>
    <!-- sqlsessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"></property>
        <property name="dataSource" ref="multipleDataSource"></property>
        <property name="mapperLocations" value="classpath:com/jdd/mapper/*.xml"></property>
    </bean>

</beans>

  8、到这里和spring相关的配置基本就配完了, 其实后面还要再配置一下 mysql的主从复制,就是对写库的操作都同步到从库,这样写库从库的数据才一致。具体配置操作我就不写了,可以自行到网上搜索。

spring 配置多数据源(mysql读写分离)的更多相关文章

  1. 使用Spring配置动态数据源实现读写分离

    最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考.关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!-- ...

  2. 阿里P7教你如何使用 Spring 配置动态数据源实现读写分离

    最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考. 关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!- ...

  3. 使用 Spring 配置动态数据源实现读写分离

    关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!--读数据源配置--><bean id="readData ...

  4. spring 配置双数据源并读写分离

    摘自 开源项目Ibase4j    关键思想在于AbstractRoutingSource 类 还有方法名称和切入点去控制使用哪个数据源    1.首先在配置文件配置多个数据源 并且交给继承自spri ...

  5. spring集成mybatis实现mysql读写分离

    前言 在网站的用户达到一定规模后,数据库因为负载压力过高而成为网站的瓶颈.幸运的是目前大部分的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库的数据更新同步到另一台服务器上. ...

  6. mybatis用spring的动态数据源实现读写分离

    一.环境: 三个mysql数据库.一个master,两个slaver.master写数据,slaver读数据. 二.原理: 借助Spring的 AbstractRoutingDataSource 这个 ...

  7. Spring Boot2(四):使用Spring Boot多数据源实现读写分离

    前言 实际业务场景中,不可能只有一个库,所以就有了分库分表,多数据源的出现.实现了读写分离,主库负责增改删,从库负责查询.这篇文章将实现Spring Boot如何实现多数据源,动态数据源切换,读写分离 ...

  8. ThinkPHP配置简单的mysql读写分离

    ThinkPHP内置了分布式数据库的支持,包括主从式数据库的读写分离,但是分布式数据库必须是相同的数据库类型. 配置DB_DEPLOY_TYPE 为1 可以采用分布式数据库支持.如果采用分布式数据库, ...

  9. 原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么

    开心一刻 女孩睡醒玩手机,收到男孩发来一条信息:我要去跟我喜欢的人表白了! 女孩的心猛的一痛,回了条信息:去吧,祝你好运! 男孩回了句:但是我没有勇气说不来,怕被打! 女孩:没事的,我相信你!此时女孩 ...

  10. Spring配置动态数据源-读写分离和多数据源

    在现在互联网系统中,随着用户量的增长,单数据源通常无法满足系统的负载要求.因此为了解决用户量增长带来的压力,在数据库层面会采用读写分离技术和数据库拆分等技术.读写分离就是就是一个Master数据库,多 ...

随机推荐

  1. Cocos2D游戏项目CCTableView在Xcode7.2下的无法滚动问题

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 一个RPG游戏转换到Xcode7.2下发现一个问题,原来可以上 ...

  2. Git管理工具对比(GitBash、EGit、SourceTree)

    Git管理工具对比(GitBash.EGit.SourceTree) GitBash是采用命令行的方式对版本进行管理,功能最为灵活强大,但是由于需要手动输入希望修改的文件名,所以相对繁琐. EGit是 ...

  3. ledisdb:支持类redis接口的嵌入式nosql

    ledisdb现在可以支持嵌入式使用.你可以将其作为一个独立的lib(类似leveldb)直接嵌入到你自己的应用中去,而无需在启动单独的服务. ledisdb提供的API仍然类似redis接口.首先, ...

  4. 如何在Cocos2D 1.0 中掩饰一个精灵(二)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 让我们开始吧 打开Xcode,从New Project中选择co ...

  5. Mybatis执行BaseExecutor(二)

    BaseExecutor是Executor的一个子类,是一个抽象类,其实现了接口Executor的部分方法,并提供了三个抽象方法doUpdate.doFlushStatements和doQuery在他 ...

  6. (六十八)使用XMPPFramework登录

    按照XMPPFramework的官方样例,应该把登录代码放置在AppDelegate中,并且让注销成为私有方法. XMPPFramework进行登录的步骤如下: ①连接主机,并且发送JID ②如果连接 ...

  7. Android项目-高考作文项目架构(二)

    1, 普通的http json请求 请看下面架构草图: 这样就抽象出了其他Activity可能需要的Http Json请求的功能. 只要其他Activity有Http Json请求的需求都可以继承Ba ...

  8. 理解WebKit和Chromium: JavaScript引擎简介

    转载请注明原文地址:http://blog.csdn.net/milado_nju 1. 什么是JavaScript引擎 什么是JavaScript引擎?简单来讲,就是能够提供执行JavaScript ...

  9. 一致性Hash算法介绍(分布式环境算法)

    32的整数环(这个环被称作一致性Hash环),根据节点名称的Hash值(其分布范围同样为0~232)将节点放置在这个Hash 环上.然后根据KEY值计算得到其Hash值(其分布范围也同样为0~232  ...

  10. Java Web 高性能开发,第 2 部分: 前端的高性能

    Web 发展的速度让许多人叹为观止,层出不穷的组件.技术,只需要合理的组合.恰当的设置,就可以让 Web 程序性能不断飞跃.Web 的思想是通用的,它们也可以运用到 Java Web.这一系列的文章, ...