ssm maven spring AOP读写分离

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.jianshu.com/p/302e1bb76710

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 124,640评论 18 136
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 41,927评论 6 343
  • application的配置属性。 这些属性是否生效取决于对应的组件是否声明为Spring应用程序上下文里的Bea...
    新签名阅读 4,893评论 1 28
  • 这些属性是否生效取决于对应的组件是否声明为 Spring 应用程序上下文里的 Bean(基本是自动配置的),为一个...
    发光的鱼阅读 1,042评论 0 14
  • 相遇的时候是个意外 离别的时候意外的看不开 人说,韶华易逝 应该先有梦想和未来 才有资格谈起生活和爱情 人说,红颜...
    夏熙入梦阅读 38评论 0 0
  • 写作者或多或少有点自负,虽然不大可能像李敖那么张狂,敢于说出“中文作家排名前三的是李敖,李敖,李敖”这种大话,但至...
    群山阅读 289评论 12 13
  • 01 三焦经的作用 三焦经既主导着气的运行,也有调节水道的重要作用,相当于人体的地下水系统。与人体的内分泌有着密切...
    雨林中的阳光阅读 11,891评论 0 15
  • 今天是姑娘上学第一次数学小测试,早晨送她上学时自己还说认真答题认真检查,这不一个不认真错了好几道题,不过还是表扬了...
    娃娃的娘亲阅读 40评论 0 0