ssm maven spring AOP读写分离

总体流程

配置最开始写在pom.xml文件,解析到数据库配置文件,再解析到spring配置文件。

自定义注解DataSource;通过这个注解并且在spring、springmv配置文件添加AOP拦截,去定义拦截函数,根据参数切换数据源。

即通过注解实现AOP拦截controller,或者service层。从而实现读写分离。

具体见代码和注释。

1,pom.xml 配置数据库部分
<profile>
<id>local</id>
<properties>
<db-url><![CDATA[jdbc:mysql://XXX?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=utf8]]></db-url>
<db-driverClassName>com.mysql.jdbc.Driver</db-driverClassName>
<db-username>XXX</db-username>
<db-password>XXX</db-password> <aliyun.oss.endpoint><![CDATA[oss-cn-beijing.aliyuncs.com]]></aliyun.oss.endpoint>
<aliyun_oss_endpointURL><![CDATA[http://oss-cn-beijing.aliyuncs.com]]></aliyun_oss_endpointURL>
<aliyun.oss.accessKeyId>LTAIz0kIEQRSBc6G</aliyun.oss.accessKeyId>
<aliyun.oss.accessKeySecret>3YS3fXPvIHng11ft7wQRGDeojP2Neg</aliyun.oss.accessKeySecret>
<aliyun.oss.templateInputBucket>iceplay</aliyun.oss.templateInputBucket>
<aliyun.oss.templateResultBucket>iceplay</aliyun.oss.templateResultBucket>
<aliyun.oss.templatePicBucket>iceplay</aliyun.oss.templatePicBucket>
<aliyun.oss.picTaskInputBucket>iceplay</aliyun.oss.picTaskInputBucket>
<aliyun.oss.picTaskResultBucket>iceplay</aliyun.oss.picTaskResultBucket>
<aliyun.oss.avatorBucket>iceplay</aliyun.oss.avatorBucket>
<aliyun.oss.adviceBucket>iceplay</aliyun.oss.adviceBucket>
<aliyun.oss.fontBucket>iceplay</aliyun.oss.fontBucket>
<aliyun.oss.picimgPath>app-image</aliyun.oss.picimgPath>
<aliyun.oss.watermark></aliyun.oss.watermark> <aliyun.mq.accessKey>LTAItRByuljwgIgI</aliyun.mq.accessKey>
<aliyun.mq.secretKey>fauyIEV8LMwSj8alFYUqEXeU5VMfwY</aliyun.mq.secretKey>
<aliyun.mq.sendMsgTimeoutMillis>3000</aliyun.mq.sendMsgTimeoutMillis>
<aliyun.mq.templateTaskProducerId>PID-Template-Task</aliyun.mq.templateTaskProducerId>
<aliyun.mq.consumerId>CID-AppServer</aliyun.mq.consumerId>
<aliyun.mq.topicTemplateTask>template-tasks</aliyun.mq.topicTemplateTask>
<aliyun.mq.topicTemplateResult>template-results</aliyun.mq.topicTemplateResult>
<aliyun.mq.topicImageCreateTask>pic-tasks</aliyun.mq.topicImageCreateTask>
<aliyun.mq.topicImageCreateResult>pic-results</aliyun.mq.topicImageCreateResult>
<aliyun.mq.imageCreateTaskProducerId>PID-Pic-Task</aliyun.mq.imageCreateTaskProducerId>
<aliyun.mq.topicServerStatus>server-status</aliyun.mq.topicServerStatus>
<aliyun.mq.serverConfigProducerId>PID-Manage</aliyun.mq.serverConfigProducerId>
<aliyun.mq.topicServerConfig>server-config</aliyun.mq.topicServerConfig>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>localSalve</id>
<properties>
<dbs-url><![CDATA[jdbc:mysql://XXX?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=utf8]]></dbs-url>
<dbs-driverClassName>com.mysql.jdbc.Driver</dbs-driverClassName>
<dbs-username>XXX</dbs-username>
<dbs-password>XXX</dbs-password> <aliyun.oss.endpoint><![CDATA[oss-cn-beijing.aliyuncs.com]]></aliyun.oss.endpoint>
<aliyun_oss_endpointURL><![CDATA[http://oss-cn-beijing.aliyuncs.com]]></aliyun_oss_endpointURL>
<aliyun.oss.accessKeyId>LTAIz0kIEQRSBc6G</aliyun.oss.accessKeyId>
<aliyun.oss.accessKeySecret>3YS3fXPvIHng11ft7wQRGDeojP2Neg</aliyun.oss.accessKeySecret>
<aliyun.oss.templateInputBucket>iceplay</aliyun.oss.templateInputBucket>
<aliyun.oss.templateResultBucket>iceplay</aliyun.oss.templateResultBucket>
<aliyun.oss.templatePicBucket>iceplay</aliyun.oss.templatePicBucket>
<aliyun.oss.picTaskInputBucket>iceplay</aliyun.oss.picTaskInputBucket>
<aliyun.oss.picTaskResultBucket>iceplay</aliyun.oss.picTaskResultBucket>
<aliyun.oss.avatorBucket>iceplay</aliyun.oss.avatorBucket>
<aliyun.oss.adviceBucket>iceplay</aliyun.oss.adviceBucket>
<aliyun.oss.fontBucket>iceplay</aliyun.oss.fontBucket>
<aliyun.oss.picimgPath>app-image</aliyun.oss.picimgPath>
<aliyun.oss.watermark></aliyun.oss.watermark> <aliyun.mq.accessKey>LTAItRByuljwgIgI</aliyun.mq.accessKey>
<aliyun.mq.secretKey>fauyIEV8LMwSj8alFYUqEXeU5VMfwY</aliyun.mq.secretKey>
<aliyun.mq.sendMsgTimeoutMillis>3000</aliyun.mq.sendMsgTimeoutMillis>
<aliyun.mq.templateTaskProducerId>PID-Template-Task</aliyun.mq.templateTaskProducerId>
<aliyun.mq.consumerId>CID-AppServer</aliyun.mq.consumerId>
<aliyun.mq.topicTemplateTask>template-tasks</aliyun.mq.topicTemplateTask>
<aliyun.mq.topicTemplateResult>template-results</aliyun.mq.topicTemplateResult>
<aliyun.mq.topicImageCreateTask>pic-tasks</aliyun.mq.topicImageCreateTask>
<aliyun.mq.topicImageCreateResult>pic-results</aliyun.mq.topicImageCreateResult>
<aliyun.mq.imageCreateTaskProducerId>PID-Pic-Task</aliyun.mq.imageCreateTaskProducerId>
<aliyun.mq.topicServerStatus>server-status</aliyun.mq.topicServerStatus>
<aliyun.mq.serverConfigProducerId>PID-Manage</aliyun.mq.serverConfigProducerId>
<aliyun.mq.topicServerConfig>server-config</aliyun.mq.topicServerConfig>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
2,数据库配置文件(部分)
config_url:${db-url}
config_driverClassName:${db-driverClassName}
config_username:${db-username}
config_password:${db-password} salve_config_url:${dbs-url}
salve_config_driverClassName:${dbs-driverClassName}
salve_config_username:${dbs-username}
salve_config_password:${dbs-password}
3,spring springmvc配置文件

spring 配置数据连接池,数据源,拦截等。


<!-- 阿里 druid数据库连接池 -->
<bean id="dataSource_write" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<!-- 数据库基本信息配置 -->
<property name="url" value="${config_url}" />
<property name="username" value="${config_username}" />
<property name="password" value="${config_password}" />
<property name="driverClassName" value="${config_driverClassName}" />
<property name="filters" value="${filters}" />
<!-- 最大并发连接数 -->
<property name="maxActive" value="${maxActive}" />
<!-- 初始化连接数量 -->
<property name="initialSize" value="${initialSize}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${maxWait}" />
<!-- 最小空闲连接数 -->
<property name="minIdle" value="${minIdle}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${validationQuery}" />
<property name="testWhileIdle" value="${testWhileIdle}" />
<property name="testOnBorrow" value="${testOnBorrow}" />
<property name="testOnReturn" value="${testOnReturn}" />
<property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="${removeAbandoned}" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="${logAbandoned}" />
</bean> <!-- 阿里 druid数据库连接池 salve add by 胜杰-->
<bean id="dataSource_read_one" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<!-- 数据库基本信息配置 -->
<property name="url" value="${salve_config_url}" />
<property name="username" value="${salve_config_username}" />
<property name="password" value="${salve_config_password}" />
<property name="driverClassName" value="${salve_config_driverClassName}" />
<property name="filters" value="${filters}" />
<!-- 最大并发连接数 -->
<property name="maxActive" value="${maxActive}" />
<!-- 初始化连接数量 -->
<property name="initialSize" value="${initialSize}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${maxWait}" />
<!-- 最小空闲连接数 -->
<property name="minIdle" value="${minIdle}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${validationQuery}" />
<property name="testWhileIdle" value="${testWhileIdle}" />
<property name="testOnBorrow" value="${testOnBorrow}" />
<property name="testOnReturn" value="${testOnReturn}" />
<property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="${removeAbandoned}" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="${logAbandoned}" />
</bean>
<!-- 配置动态数据源,并设置默认的数据源。如果想要在类上加注解,则要去掉这个默认的数据源。 -->
<bean id="dataSource" class="com.fh.dataexchanger.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- write -->
<entry key="write" value-ref="dataSource_write"/>
<!-- read -->
<entry key="read" value-ref="dataSource_read_one"/>
</map> </property>
<property name="defaultTargetDataSource" ref="dataSource_write"/>
</bean>
<!--第一个 * —— 通配 任意返回值类型
第二三个 * —— 通配 包com.(任意).service下的任意class
第四个 * —— 通配 包com.evan.crm.service下的任意class的任意方法
第四个 .. —— 通配 方法可以有0个或多个参数 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="dataSourceAspect" class="com.fh.dataexchanger.DataSourceAspect" />
<aop:config>
<aop:aspect id="c" ref="dataSourceAspect">
<aop:pointcut id="tx"
expression="execution(* com.*.controller..*.*(..))" />
<aop:before pointcut-ref="tx" method="before" />
</aop:aspect>
</aop:config>

spring mvc:加这一句主要是为了使之可以拦截controller


<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy />

加上边一句的同时头部要加上下边三句到相应的位置。

xmlns:aop="http://www.springframework.org/schema/aop"  

    http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

4,java 数据源文件

定义一个名为DataSource的注解,Target用于指定注解可以添加的位置。两个参数的意思是:类和接口、方法。Retention 的作用时指定注解运行时间,运行时。


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

定义AOP切面以便拦截所有带有注解@DataSource的方法,取出注解的值作为数据源标识放到DynamicDataSourceHolder的线程变量中

public class DataSourceAspect {

   public void before(JoinPoint point){
Object target = point.getTarget();
String method = point.getSignature().getName();
System.out.println(method);
Class<?> classz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes(); try {
Method m = classz.getDeclaredMethod(method, parameterTypes);
if(m != null && m.isAnnotationPresent(DataSource.class)){
DataSource ds = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.putDataSource(ds.value());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

Spring 的AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey,这个lookupKey就是数据源标识。

因此通过重写这个查找数据源标识的方法就可以让spring切换到指定的数据源了。

创建一个DynamicDataSource的类,继承AbstractRoutingDataSource并重写determineCurrentLookupKey方法,

public class DynamicDataSource extends AbstractRoutingDataSource{

	@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSource();
} }

创建DynamicDataSourceHolder用于持有和设置当前线程中使用的数据源标识

public class DynamicDataSourceHolder {

	public static final ThreadLocal<String> holder = new ThreadLocal<String>();

	public static void putDataSource(String name){
holder.set(name);
} public static String getDataSource(){
return holder.get();
}
}

参考:http://www.cnblogs.com/liujiduo/p/5004691.html

http://blog.csdn.net/u010004317/article/details/47700447

http://blog.csdn.net/mfc2003/article/details/48490151

----名白

转载注明出处:http://www.cnblogs.com/mingbai/p/writeread.html

ssm maven spring AOP读写分离的更多相关文章

  1. Spring aop读写分离

    一.采用读写分离技术的目标 随着网站的业务不断扩展,数据不断增加,用户越来越多,数据库的压力也就越来越大,采用传统的方式,比如:数据库或者SQL的优化基本已达不到要求,这个时候可以采用读写分离的策略来 ...

  2. java 使用spring实现读写分离

    最近上线的项目中数据库数据已经临近饱和,最大的一张表数据已经接近3000W,百万数据的表也有几张,项目要求读数据(select)时间不能超过0.05秒,但实际情况已经不符合要求,explain建立索引 ...

  3. spring实现读写分离

    (转自:http://www.cnblogs.com/surge/p/3582248.html) 现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数 ...

  4. 使用Spring实现读写分离( MySQL实现主从复制)

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt403 1.  背景 我们一般应用对数据库而言都是"读多写少&quo ...

  5. Spring 数据库读写分离

    读写分离常见有俩种方式 1 第一种方式比较常用就是定义2个数据库连接,一个是Master,另一个是Slave.更新数据时我们取Master,查询数据时取Slave.太过简单不做介绍. 2 第二种方数据 ...

  6. Spring + Mybatis 读写分离

    项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库. 实现思路是: 第一步,实现动态切换数据源:配置两个DataSource,配置两个SqlSessionFactory指向两 ...

  7. 使用Spring实现读写分离( MySQL实现主从复制)(转)

    本文转自:http://blog.csdn.net/jack85986370/article/details/51559232 1.  背景 我们一般应用对数据库而言都是“读多写少”,也就说对数据库读 ...

  8. 002-使用Spring实现读写分离(MySQL实现主从复制)

    一. 背景 一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大主库,负责写入数据,我们称之为:写库:从库,负责读取数据,我们称之为:读库: 1. 读库和写库的数据一致:2. 写数 ...

  9. [转]Spring数据库读写分离

    数据库的读写分离简单的说是把对数据库的读和写操作分开对应不同的数据库服务器,这样能有效地减轻数据库压力,也能减轻io压力. 主(master)数据库提供写操作,从(slave)数据库提供读操作,其实在 ...

随机推荐

  1. Django学习(3)模板定制

    在Django学习(一)一首情诗中,views.py中HTML被直接硬编码在代码之中,虽然这样便于解释视图是如何工作的,但直接将HTML硬编码到视图却不算一个好主意.因为: 对页面设计进行的任何改变都 ...

  2. Struts2学习---namespace,file模块包含,默认action

    我们上一节已经将action基本的配置和使用讲了,接下来我们讲以下struts一些小知识点: namespac: 上一节学习action的时候我们访问我们jsp文件时候使用的: http://loca ...

  3. ABP 找不到版本为 (>= 1.0.0-preview1-27891) 的包 Microsoft.AspNetCore.SignalR 错误

    错误描述: 下载ABP模板项目3.4.1的版本(当前最新版本),编译加载nuget包Microsoft.AspNetCore.SignalR时会报如下错误: 严重性     代码         说明 ...

  4. c#中treeview的使用方法(转 )

    本文主要介绍treeView控件中,添加,修改.删除节点的操作, 首先当窗体加载的时候,我们添加上图中所示的节点. 当点击“Delete the Selected”按钮时,被选中的节点将被删除. 当点 ...

  5. csdn博客被删除联系客服恢复

    前几天写了篇"如何使用shadowsocksFQ",居然被删除,我很郁闷,客服给我的回答是"影响了客户体验,网安查的严,不能带有FQ的信息出现" 我一直很郁闷, ...

  6. bzoj 4199 [NOI2015]寿司晚宴

    Description 为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴.小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴. 在晚宴上,主办方为大家提供了 n−1 种不同 ...

  7. Java集合(一) CopyOnWriteArrayList

    CopyOnWriteArrayList 类分析   1. CopyOnWriteArrayList 其中底层实现存放数据是一个Object数组:   private volatile transie ...

  8. NOI 2008 假面舞会

    题目描述 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会. 今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具.每个面具都有一个编号,主办方会把此编号 ...

  9. 【原创】重复造轮子之高仿EntityFramework

    前言 在上一篇<[原创]打造基于Dapper的数据访问层>中,Dapper在应付多表自由关联.分组查询.匿名查询等应用场景时经常要手动写SQL语句.看着代码里满屏的红色SQL字符串,简直头 ...

  10. 第九章 BootstrapTable的使用

    一.简介 BootstrapTable是一个Bootstrap 3 的表格插件,支持单选, 复选框, 排序, 分页等功能 官网:http://bootstrap-table.wenzhixin.net ...